使用主键导致死锁的两个重复删除查询

时间:2012-06-04 16:20:50

标签: mysql locking deadlock

我不明白两个重复查询如何使用主键对单个表删除单个行可能会出现死锁。谁能解释一下?

在我看来,其中一个交易应该获得锁定而另一个交易必须等待。

这是死锁报告,包含查询:

Fri Jun 01 2012 13:50:23
*** (1) TRANSACTION:
TRANSACTION 3 1439005348, ACTIVE 0 sec, process no 22419, OS thread id 1166235968 starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 368
MySQL thread id 125597624, query id 3426379709 node3-int 10.5.1.119 application-devel updating
DELETE FROM `SessData` WHERE `SessKey` = '87EDF1479A275557AC8280DCA78AB886'
AND `Name` = 'CurrentRequestURL'

*** (2) TRANSACTION:
TRANSACTION 3 1439005340, ACTIVE 0 sec, process no 22419, OS thread id 1234073920 starting index read, thread declared inside InnoDB 0
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1216
MySQL thread id 125597622, query id 3426379705 node2-int 10.5.1.118 application-devel updating
DELETE FROM `SessData` WHERE `SessKey` = '87EDF1479A275557AC8280DCA78AB886'
AND `Name` = 'CurrentRequestURL'

*** WE ROLL BACK TRANSACTION (2)

这是表格的架构:

CREATE TABLE  `application`.`SessData` (
  `SessKey` varchar(255) NOT NULL default '',
  `Name` varchar(255) NOT NULL default '',
  `Value` varchar(255) default NULL,
  PRIMARY KEY  (`SessKey`,`Name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

其他一些细节:

MySQL version: 4.1.21
Isolation level: REPEATABLE-READ
Character set on the the above columns: latin1

3 个答案:

答案 0 :(得分:4)

您使用的是MySQL 4.1.21版。 4.1已超过其寿命终止,4.1.21甚至不是最新的4.1版本。 (Extended support for MySQL 4.1 ended on December 31, 2009。)你应该升级到至少5.0.96,尽管你也可以完全更新到5.5.25。如果不这样做,升级到4.1.22将是您可以做的最小值,尽管这可能无法解决您的问题。

如果您阅读了MySQL 4.1 documentation中的最后一个示例,您会看到如果先前已在事务中使用共享锁选择了要删除的行,则会发生此死锁。同样,如果涉及外键约束,您可能已获得共享锁。一般问题是:

  

A在x

上获取共享锁      

B等待x的独占锁定。由于A的锁定,它必须等待。

     

等待x上的独占锁定。它必须等待,因为B在队列中位于独占锁之前。

InnoDB处理锁的方式,当B等待相同的独占锁时,它不会将A的共享锁升级为独占锁,因此这是一个死锁。

另外,当两个语句都试图删除不存在的行(可能刚刚被前一个第三个重复删除删除)时,您可能会遇到错误。可能与:

有关

答案 1 :(得分:1)

正如the manual所述:

  

DELETE FROM ... WHERE ...为搜索遇到的每条记录设置一个独占的下一键锁定。

Elsewhere,它解释道:

  

下一键锁定:这是索引记录上的记录锁定和索引记录之前的间隙上的间隙锁定的组合。

由于涉及两个以上的锁,一个连接可能获得一个这样的锁,而另一个连接获得另一个;然后他们就会陷入僵局,等待释放他们没有锁定的锁。

虽然您可以使用READ COMMITTED隔离级别禁用间隙锁定,但这确实会使您显示phantom rows。您最好检测并重新发出在发生死锁时失败的事务(因为在这种情况下,成功的事务将删除记录,因此不需要重新发送回滚事务)。

答案 2 :(得分:1)

我记得大约一年前帮助某人解决了类似的僵局问题

不容忽视的是,InnoDB死锁可能是由特定情况下的SELECT语句引起的:https://dba.stackexchange.com/questions/4469/are-innodb-deadlocks-exclusive-to-insert-update-delete/4470#4470(2011年8月8日)

请查看您的SHOW INNODB ENGINE STATUS\G。由于您使用的是MySQL 4.1,因此信息并不完整,以便揭示问题。

尽管如此,这里发生了什么?您基本上是locking the Clustered Index(也称为gen_clust_index)。同一行上的两个锁将锁定聚集索引中的同一行及其gen_clust_index条目。

只能锁定一个。另一个将被锁定为独占但等待。当然,最后一次独家锁定必须获胜。如果两个事务同时超时,则必须在50秒内(innodb_lock_wait_timeout的缺省值)发生一个或两个事务。

那么,谁回滚了?根据您的MySQL 4.1 SHOW INNODB ENGINE STATUS\GTRANSACTION (2)会因为TRANSACTION (1)首先获得群集密钥条目而阻止灰尘和回滚。