毫不夸张的说,有一半以上的死锁问题由唯一索引贡献,后面介绍的很多死锁的问题都跟唯一索引有关。这次我们讲一段唯一索引 S 锁与 X 锁的爱恨情仇
我们来看一个简化过的例子
# 构造数据
CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10),
`level` int(11),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name` (`name`)
);
INSERT INTO `t1` (`name`, `level`) VALUES ('A',0);
# 出现问题的sql语句如下,并发情况下就会出现死锁
INSERT ignore INTO `t1` (`name`, `level`) VALUES ('A',0);
update t1 set level = 1 where name = "A";
我们用之前介绍过的源码分析方式,先来看下这两条语句分别加什么锁,然后分析死锁形成的过程。
第一条语句
INSERT ignore INTO t1 (name, level) VALUES ('A',0);
在调试中得到的结果如下
可以看到这条语句对唯一键 uk_name 加共享锁(S锁),而且成功。
第二条语句
update t1 set level = 1 where name = "A"; 通过唯一键更新数据库字段。
这种情况在之前的文章已经介绍过,会对唯一索引加 X 锁,然后对主键索引加 X 锁
这样就可以非常轻松的复现死锁的问题了,步骤如下
开启两个 session,分别 begin
session1 执行INSERT ignore INTO t1 (name, level) VALUES ('A',0);
session2 执行INSERT ignore INTO t1 (name, level) VALUES ('A',0);
session1 执行update t1 set level = 1 where name = "A"; 进入等待状态
session2 执行update t1 set level = 1 where name = "A";,死锁产生,被回滚,同时事务 1 执行成功
详细的锁状态变化如下
t1
t2
备注
INSERT IGNORE INTO
-
t1成功获得uk的S锁 DB_SUCCESS
-
INSERT IGNORE INTO
t2成功获得uk的S锁 DB_SUCCESS
UPDATE
-
t1尝试获得uk的X锁,但没有成功,处于等待状态 DB_LOCK_WAIT
-
UPDATE
t2尝试获得uk的X锁,发现死锁产生 DB_DEADLOCK
-
Deadlock
t2释放S锁
成功
-
-
死锁日志如下: