9

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

在我看来,其中一项交易应该已获得锁定,而另一笔交易将不得不等待。

这是带有查询的死锁报告:

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
4

3 回答 3

4

您正在使用 MySQL 版本 4.1.21。4.1 已经结束生命周期,4.1.21 甚至不是最新的 4.1 版本。(对 MySQL 4.1 的扩展支持于 2009 年 12 月 31 日结束。)您应该至少升级到 5.0.96,尽管您最好完全更新到 5.5.25。如果做不到这一点,升级到4.1.22将是您可以做的最低要求,尽管这可能无法解决您的问题。

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

A 在 x 上获取共享锁

B 等待 x 的排他锁。由于 A 的锁,它必须等待。

A 等待 x 上的排他锁。它必须等待,因为 B 在排他锁的队列中领先于它。

InnoDB 处理锁的方式,在 B 等待同一个排他锁的时候,不会将 A 的共享锁升级为排他锁,所以这是一个死锁。

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

于 2012-06-08T20:33:57.207 回答
1

手册所述:

DELETE FROM ... WHERE ...在搜索遇到的每条记录上设置一个排他的 next-key 锁。

在其他地方,它解释说:

Next-key lock:这是索引记录上的记录锁和索引记录之前的间隙上的间隙锁的组合。

由于涉及两个以上的锁,一个连接可能获得一个这样的锁,而另一个连接获得另一个;然后他们将陷入死锁,等待对方释放他们不持有的锁。

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

于 2012-06-04T17:00:00.357 回答
1

我记得大约一年前帮助某人解决了类似的死锁情况

不容忽视的是,在特定情况下,SELECT 语句可能会导致 InnoDB 死锁: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,因此信息并不完整,无法揭示问题。

尽管如此,这里发生了什么?您实际上是在锁定聚集索引(也称为gen_clust_index)。同一行上的两个锁会锁定同一行及其在聚集索引中的 gen_clust_index 条目。

只有一个可以独占锁定。另一个将被锁定为独占但正在等待。当然,最后一个独占锁必须赢。如果两个事务同时超时,一个或两个事务必须在 50 秒内发生(默认为 innodb_lock_wait_timeout)。

那么,谁会回滚呢?根据您的 MySQL 4.1 SHOW INNODB ENGINE STATUS\GTRANSACTION (2)会咬住灰尘并回滚,因为TRANSACTION (1)首先获得了集群键条目。

于 2012-06-14T17:02:18.747 回答