InnoDB的锁机制深入理解(8)

当事务一执行回滚时,事务二和事务三发生了死锁。InnoDB的死锁检测一旦检测到死锁发生,会自动失败其中一个事务,因此看到的结果是一个失败另一个成功。

为什么会死锁?

死锁产生的原因是事务一插入记录时,对(2,2)记录加X锁,此时事务二和事务三插入数据时检测到了重复键错误,此时事务二和事务三要在这条索引记录上设置S锁,由于X锁的存在,S锁的获取被阻塞。
事务一回滚,由于S锁和S锁是可以兼容的,因此事务二和事务三都获得了这条记录的S锁,此时其中一个事务希望插入,则该事务期望在这条记录上加上X锁,然而另一个事务持有S锁,S锁和X锁互相是不兼容的,两个事务就开始互相等待对方的锁释放,造成了死锁。

事务二和事务三为什么会加S锁,而不是直接等待X锁

事务一的insert语句加的是隐式锁(隐式的Record锁、X锁),但是其他事务插入同一行记录时,出现了唯一键冲突,事务一的隐式锁升级为显示锁。
事务二和事务三在插入之前判断到了唯一键冲突,是因为插入前的重复索引检查,这次检查必须进行一次当前读,于是非唯一索引就会被加上S模式的next-key锁,唯一索引就被加上了S模式的Record锁。
因为插入和更新之前都要进行重复索引检查而执行当前读操作,所以RR隔离级别下,同一个事务内不连续的查询,可能也会出现幻读的效果(但个人并不认为RR级别下也会出现幻读,幻读的定义应该是连续的读取)。而连续的查询由于都是读取快照,中间没有当前读的操作,所以不会出现幻读。

6.1.2 commit引发的Duplicate key死锁

delete-insert-insert-commit死锁

事务一事务二事务三
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> delete from test where id=2;
Query OK, 1 row affected (0.01 sec)
         
    mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test values (2,2);
执行之后被阻塞,等待事务一
     
        mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test values (2,2);
执行之后被阻塞,等待事务一
 
mysql>commit;
Query OK, 0 rows affected (0.00 sec)
         
    ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction      
        Query OK, 1 row affected (2.37 sec)  

这种情况下产生的死锁和insert-insert-insert-rollback死锁产生的原理一致。

6.2 数据插入的过程

经过以上分析,一条数据在插入时经过以下几个过程:
假设数据表test.test中存在(1,1)、(5,5)和(10,10)三条记录。

事务开启,尝试获取插入意向锁。例如,事务一执行了select * from test where id>8 for update,事务二要插入(9,9),此时先要获取插入意向锁,由于事务一已经在对应的记录和间隙上加了X锁,因此事务二被阻塞,并且阻塞的原因是获取插入意向锁时被事务一的X锁阻塞。

获取意向锁之后,插入之前进行重复索引检查。重复索引检查为当前读,需要添加S锁。

如果是已经存在唯一索引,且索引未加锁。直接抛出Duplicate key的错误。如果存在唯一索引,且索引加锁,等待锁释放。

重复检查通过之后,加入X锁,插入记录

6.3 GAP与Insert Intention冲突引发死锁 update-insert死锁

仍然是表test,当前表中的记录如下:

mysql> select * from test; +----+------+ | id | code | +----+------+ | 1 | 1 | | 5 | 5 | | 10 | 10 | +----+------+ 3 rows in set (0.01 sec) 事务一事务二
begin;   begin;  
select * from test where id=5 for update;   select * from test where id=10 for update;  
insert into test values(7,7);      
    insert into test values(7,7);  
Query OK, 1 row affected (5.03 sec)      
    ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction  

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/d7c44bb9ed60b10c737e877a225bbadf.html