15

我发现了一个非常令人困惑的僵局情况,我需要帮助才能理解。

有两个事务正在进行:
(2) 为查询持有一个锁delete from myTable where id = NAME_CONST('p_id',10000)。这是 PRIMARY KEY 的锁,虽然不是完整的密钥,而是一个范围。看起来这对我来说是一个完整的写锁lock_mode X locks rec but not gap
(1) 正在等待同一个锁,也等待查询delete from myTable where id = NAME_CONST('p_id',10000)
(2) 也在尝试获取这个锁,MySQL 检测到死锁。

我无法理解的是为什么(2)必须再次获取锁,因为它已经持有它并且在所有情况下它都是一个写锁(lock_mode X)。

它看起来也适用于完全相同的查询。

这是表定义

create myTable (
  id int unsigned not null,
  value1 char(8) not null,
  value2 int unsigned,
  primary key (id, value1)
);

这是来自的信息SHOW ENGINE INNODB STATUS\G

------------------------
LATEST DETECTED DEADLOCK
------------------------
130313 14:46:28
*** (1) TRANSACTION:
TRANSACTION 75ACB8A3, ACTIVE 0 sec, process no 6110, OS thread id 139973945382656 starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 5154970, query id 5201313618 192.168.0.2 user updating
delete from myTable where id = NAME_CONST('p_id',10000)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 22371 page no 1598 n bits 104 index `PRIMARY` of table `db`.`myTable` trx id 75ACB8A3 lock_mode X waiting
Record lock, heap no 32 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 4; hex 0005af3a; asc :;;
1: len 8; hex 2020202020202020; asc ;;
2: len 6; hex 000075acb890; asc u ;;
3: len 7; hex ea0000020d011e; asc ;;
4: len 4; hex 00000065; asc e;;

*** (2) TRANSACTION:
TRANSACTION 75ACB890, ACTIVE 0 sec, process no 6110, OS thread id 139973957895936 starting index read
mysql tables in use 1, locked 1
7 lock struct(s), hea
p size 1248, 6 row lock(s), undo log entries 4
MySQL thread id 5155967, query id 5201313625 192.168.0.1 user updating
delete from myTable where id = NAME_CONST('p_id',10000)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 22371 page no 1598 n bits 104 index `PRIMARY` of table `db`.`myTable` trx id 75ACB890 lock_mode X locks rec but not gap
Record lock, heap no 32 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 4; hex 0005af3a; asc :;;
1: len 8; hex 2020202020202020; asc ;;
2: len 6; hex 000075acb890; asc u ;;
3: len 7; hex ea0000020d011e; asc ;;
4: len 4; hex 00000065; asc e;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 22371 page no 1598 n bits 104 index `PRIMARY` of table `db`.`myTable` trx id 75ACB890 lock_mode X waiting
Record lock, heap no 32 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 4; hex 0005af3a; asc :;;
1: len 8; hex 2020202020202020; asc ;;
2: len 6; hex 000075acb890; asc u ;;
3: len 7; hex ea0000020d011e; asc ;;
4: len 4; hex 00000065; asc e;;

*** WE ROLL BACK TRANSACTION (1)
4

1 回答 1

27

它不是同一个锁 - 锁事务 1 仅在(索引)记录上,而不是间隙锁。

这是正在发生的事情:

  1. 事务 2 获得了(索引)记录的锁,但没有记录之前的间隙('rec but not gap'),即它只有一个记录锁。
  2. 事务 1 尝试获取记录上的锁和之前的间隙(即下一个键锁),但不能因为事务 2 有记录锁(因此事务 1 等待)。
  3. 事务 2 尝试获取记录上的锁和之前的间隙(即下一个键锁)并且不能,因为事务 1 已经在等待同一个锁并且在队列中领先于它。
  4. 僵局。

我不完全确定为什么事务 2 没有立即获取下一个键锁——也许获取记录锁和间隙锁的过程不是原子的(在这个词的一般意义上)。

我认为问题在于您有一个复合主键 (id, value1) 但您正在从一个范围中删除(仅指定 id) - 这需要间隙锁。请参阅http://dev.mysql.com/doc/refman/5.0/en/innodb-record-level-locks.html,特别是:

使用唯一索引锁定行以搜索唯一行的语句不需要间隙锁定。(这不包括搜索条件仅包括多列唯一索引的某些列的情况;在这种情况下,确实会发生间隙锁定。)

您可以更改您的代码,以便在删除时指定完整的主键,即 idvalue1?

其他选项:

  • 当出现死锁时重试删除,例如捕获代码中的错误,如果它是由死锁引起的,则重试。这种方法说起来容易做起来难,尤其是在遗留应用程序中,但 MySQL 页面推荐了如何处理死锁

如果由于死锁而失败,请始终准备好重新发出事务。死锁并不危险。再试一次。

  • 在发出删除语句之前,用表级锁锁定整个表。但是,这可能会影响性能,并且是一种“大锤”方法。
于 2013-03-18T13:09:26.490 回答