Skip to content

Latest commit

 

History

History
72 lines (58 loc) · 3.63 KB

3.md

File metadata and controls

72 lines (58 loc) · 3.63 KB

delete-wait-lock-mode-x-locks-rec-but-not-gap-vs-delete-wait-lock-mode-x-holds-lock-mode-x

死锁特征

  1. delete WAITING FOR lock_mode X locks rec but not gap
  2. delete WAITING FOR lock_mode X, HOLDS lock_mode X
  3. 隔离级别:RR、RC

死锁日志

------------------------
LATEST DETECTED DEADLOCK
------------------------
*** (1) TRANSACTION:
TRANSACTION 1E7D49CDD, ACTIVE 69 sec fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1248, 4 row lock(s), undo log entries 1
MySQL thread id 1385867, OS thread handle 0x7fcebd956700, query id 837909262 10.246.145.78 im_mobile updating
delete from offmsg_0007 WHERE target_id = 'Y25oaHVwYW7mmZbmmZblpKnkvb8=' and gmt_modified <= '2012-12-14 15:07:14'
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 203 page no 475912 n bits 88 index `PRIMARY` of table `im_mobile`.`offmsg_0007` trx id 1E7D49CDD lock_mode X locks rec but not gap waiting
*** (2) TRANSACTION:
TRANSACTION 1E7CE0399, ACTIVE 1222 sec fetching rows, thread declared inside InnoDB 272
mysql tables in use 1, locked 1
1346429 lock struct(s), heap size 119896504, 11973543 row lock(s), undo log entries 1
MySQL thread id 1090268, OS thread handle 0x7fcebf48c700, query id 837483530 10.246.145.78 im_mobile updating
delete from offmsg_0007 WHERE target_id = 'Y25oaHVwYW7niLHkuZ3kuYU5OQ==' and gmt_modified <= '2012-12-14 14:13:28'
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 203 page no 475912 n bits 88 index `PRIMARY` of table `im_mobile`.`offmsg_0007` trx id 1E7CE0399 lock_mode X
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 203 page no 1611099 n bits 88 index `PRIMARY` of table `im_mobile`.`offmsg_0007` trx id 1E7CE0399 lock_mode X waiting

表结构

CREATE TABLE `msg` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `target_id` varchar(100) COLLATE utf8_bin NOT NULL ,
  `flag` tinyint(4) NOT NULL ,
  `gmt_create` datetime NOT NULL,
  `gmt_modified` datetime NOT NULL,
  `datablob` blob,
  `nickname` varchar(64) COLLATE utf8_bin DEFAULT NULL ,
  `source` tinyint(4) DEFAULT NULL ,
  PRIMARY KEY (`id`),
  KEY `idx_o_tid` (`target_id`,`gmt_modified`,`source`,`flag`)
) ENGINE=InnoDB

重现步骤

Session 1 Session 2
delete from offmsg_0007 WHERE target_id = 'Y25oaHVwYW7niLHkuZ3kuYU5OQ==' and gmt_modified <= '2012-12-14 14:13:28' delete from offmsg_0007 WHERE target_id = 'Y25oaHVwYW7mmZbmmZblpKnkvb8=' and gmt_modified <= '2012-12-14 15:07:14'

分析

从死锁日志里可以看出事务二已经持有了 page no 475912 主键上的 next-key 锁,并想要继续获取 page no 1611099 主键上的 next-key 锁,并且这个事务状态为 fetching rows,可以推测出事务二正在走主键扫描。而事务一要获取的锁为主键上的记录锁(lock_mode X locks rec but not gap),这是 delete 语句走二级索引正常情况下的加锁模式。

那么为什么事务二没有走二级索引?原因很可能是事务二的 WHERE 条件匹配的数据行太多,这种情况 MySQL 的优化器会认为走二级索引回表效率低,还不如直接走主键全表扫描。

虽然两个 delete 语句很类似,但是由于走不同索引,对主键索引的加锁顺序的不一致将导致死锁。

参考

  1. [MySQL 学习] Innodb锁系统(1)之如何阅读死锁日志
  2. [MySQL 学习] Innodb锁系统(2)关键函数路径
  3. [MySQL学习] Innodb锁系统(3)关键结构体及函数
  4. [MySQL学习] Innodb锁系统(4) Insert/Delete 锁处理及死锁示例分析