首页 > mysql死锁的问题

mysql死锁的问题


-- v5.6.15 -- 事务隔离级别 Repeatable Read -- 测试表结构 CREATE TABLE `test_tbl` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `key1` int(10) NOT NULL, `key2` int(10) NOT NULL, `name` char(32) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_key` (`key1`,`key2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 -- 测试数据 INSERT INTO `test_tbl` (`key1`, `key2`, `name`) VALUES (10, 20, 'apple'), (30, 40, 'apple'), (50, 60, 'apple') -- session1开始事务T1 UPDATE `test_tbl` SET `name` = 'banana' WHERE `name` = 'apple' AND `key1` = 30 -- session2开始事务T2 UPDATE `test_tbl` SET `name` = 'banana' WHERE `name` = 'apple' AND `key1` = 30 -- session1中的事务T1将key2的值更新成30后T2报错: ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction -- 如果将key2的值更新为50则没有问题 UPDATE `test_tbl` SET `key2`= 30 WHERE `key1` = 30 AND `key2` = 40

个人大概的理解是:

根据http://dev.mysql.com/doc/refman/5.6/en/innodb-record-level-locks.html所述

Gap locking is not needed for statements that lock rows using a unique index to search for a unique row. (This does not include the case that the search condition includes only some columns of a multiple-column unique index; in that case, gap locking does occur.)

  1. T1执行的语句将申请到索引记录(30, 40)上的写锁以及其之前的Gap上的写锁。
  2. T2是同样的语句,申请同样的锁产生冲突, 需要将进入等待队列。
  3. T1再次执行update语句, 需要申请索引记录(30, 40)上的写锁, 由于在第一步中T1已经持有了该锁按理应该不会死锁。 不知道是否理解有误。

问题:
1. 造成这种情况下死锁的原因是什么?
2. 这样的死锁如何避免? (目前试了下在key1上单独增加一个索引就不会死锁, 这是不是一个合理的解决办法呢?)


辅助索引的up其实是先insert再删除原来的记录。

UPDATE `test_tbl` SET `key2`= 30 WHERE `key1` = 30 AND `key2` = 40

这句会在(30,40)前插入一个(30,30),再删除(30,40),所以这里需要一个插入意向锁。

在T2执行同样语句时,对于(30,40)上的记录锁等待,但会成功Gap锁(Gap锁是全兼容的)。
那么T1的第二句要(30,40)的I锁,T2持有(30,40)的G锁。死锁了。

【热门文章】
【热门文章】