为什么提交不会导致死锁

时间:2016-08-26 01:11:28

标签: mysql database innodb

我已阅读有关InnoDB锁的mysql参考手册。我明白了 : (从: http://dev.mysql.com/doc/refman/5.5/en/innodb-locks-set.html

INSERT在插入的行上设置独占锁。此锁是索引记录锁,而不是下一键锁(即没有间隙锁),并且不会阻止其他会话在插入行之前插入间隙。

在插入行之前,设置一种称为插入意图间隙锁定的间隙锁定。该锁定表示以这样的方式插入的意图:如果插入到相同索引间隙中的多个事务不插入间隙内的相同位置,则不需要等待彼此。假设存在值为4和7的索引记录。尝试插入值5和6的单独事务在获取插入行上的排它锁之前使用插入意图锁定锁定4和7之间的间隙,但不因为行是非冲突的,所以互相阻塞。

如果发生重复键错误,则设置重复索引记录上的共享锁。如果有多个会话尝试插入同一行,如果另一个会话已经具有独占锁,则使用共享锁可能导致死锁。如果另一个会话删除该行,则会发生这种情况。假设InnoDB表t1具有以下结构:

CREATE TABLE t1(i INT,PRIMARY KEY(i))ENGINE = InnoDB; 现在假设三个会话按顺序执行以下操作:

第1节:

START TRANSACTION;
INSERT INTO t1 VALUES(1);

第二节:

START TRANSACTION;
INSERT INTO t1 VALUES(1);

第3节:

START TRANSACTION;
INSERT INTO t1 VALUES(1);

第1节:

ROLLBACK;

会话1的第一个操作获取该行的排他锁。会话2和3的操作都会导致重复键错误,并且它们都请求该行的共享锁。当会话1回滚时,它会释放对该行的独占锁定,并且会话2和3的排队共享锁请求被授予。此时,会话2和3死锁:由于另一方持有共享锁,两者都无法获取该行的独占锁。

我做了一个实验,我发现这是事实。

我的问题是:

(1)我发现,如果我提交会话1,并且没有发生死锁。为什么?当我提交会话1时,X行锁定也将被释放。所以我不明白。

THX。

1 个答案:

答案 0 :(得分:0)

我怀疑这是一个不值得提高效率的边缘案例。请注意,您必须尝试3次尝试抓住同一行 - 这种情况很少发生。你做了一次ROLLBACK - 也很少见。所以发生的僵局是矫枉过正,但不值得修复。因此,必须准备好处理各地的死锁。

仅供参考,如果这是Galera集群的3个节点,那么代码必须处理COMMIT上的错误。我怀疑如果你将这个事务(带有回滚或提交)多次应用到多个节点,会发生更多奇怪事情的组合。

现在问你的问题......大概是死锁没有发生,因为其中一个线程获得了排他锁,另一个线程被“等待”而不是“死锁”命中。