【转载】死锁日志解读

下面是日志内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
------------------------
LATEST DETECTED DEADLOCK
------------------------
2017-09-09 22:34:13 7f78eab82700
*** (1) TRANSACTION: #事务1
TRANSACTION 462308399, ACTIVE 33 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 3525577, OS thread handle 0x7f896cc4b700, query id 780039657 localhost root updating
delete from ty where a=5
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 219 page no 4 n bits 72 index `idxa` of table `test`.`ty` trx id 462308399 lock_mode X waiting
*** (2) TRANSACTION:
TRANSACTION 462308398, ACTIVE 61 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1184, 4 row lock(s), undo log entries 2
MySQL thread id 3525490, OS thread handle 0x7f78eab82700, query id 780039714 localhost root update
insert into ty(a,b) values(2,10)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 219 page no 4 n bits 72 index `idxa` of table `test`.`ty` trx id 462308398 lock_mode X
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 219 page no 4 n bits 72 index `idxa` of table `test`.`ty` trx id 462308398 lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (1)

下面分成:(1)TRANSACTION、(1)WAITING FOR THIS LOCK TO BE GRANTED、(2)TRANSACTION、(2)HOLDS THE LOCK(S)、(2)WAITING FOR THIS LOCK TO BE GRANTED、WE ROLL BACK TRANSACTION (1)
1代表事务1,2代表事务2

  • **(1)TRANSACTION: #事务1 **

TRANSACTION 462308399, ACTIVE 33 sec starting index read
事务编号为 462308399 ,活跃33秒,starting index read 表示事务状态为根据索引读取数据。
常见的其他状态:

  • fetching rows 表示事务状态在row_search_for_mysql中被设置,表示正在查找记录。
  • updating or deleting 表示事务已经真正进入了Update/delete的函数逻辑(row_update_for_mysql)
  • thread declared inside InnoDB 说明事务已经进入innodb层。通常而言 不在innodb层的事务大部分是会被回滚的。

mysql tables in use 1, 说明当前的事务使用一个表,locked 1 表示表上有一个表锁,对于DML语句为LOCK_IX
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
LOCK WAIT表示正在等待锁, 2 lock struct(s) 表示trx->trx_locks锁链表的长度为2,每个链表节点代表该事务持有的一个锁结构,包括表锁,记录锁以及auto_inc锁等。本案例中2locks 表示IX锁和lock_mode X(Next-key lock)
heap size 360 表示事务分配的锁堆内存大小,一般没有什么具体的用处。
1 row lock(s)表示当前事务持有的行记录锁/gap 锁的个数。

delete from ty where a=5 表示事务1在执行的sql

  • ** (1)WAITING FOR THIS LOCK TO BE GRANTED**

RECORD LOCKS space id 219 page no 4 n bits 72 index idxa of table test.ty trx id 462308399 lock_mode X waiting
RECORD LOCKS 表示记录锁,space id为219,page号4 ,n bits 72表示这个聚集索引记录锁结构上留有72个Bit位
表示事务1 正在等待表 ty 上的 idxa 的 X 锁本案例中其实是Next-Key lock
事务2的log 和上面分析类似

  • ** (2)TRANSACTION、HOLDS THE LOCK(S) **

** RECORD LOCKS space id 219 page no 4 n bits 72 index idxa of table test.ty trx id 462308398 lock_mode X **
显示了事务2 insert into ty(a,b) values(2,10)持有了a=5 的Lock mode X |LOCK_GAP ,不过我们从日志里面看不到 事务2 执行的 delete from ty where a=5;这点也是造成DBA 仅仅根据日志难以分析死锁的问题的根本原因。

  • ** (2)WAITING FOR THIS LOCK TO BE GRANTED**

** RECORD LOCKS space id 219 page no 4 n bits 72 index idxa of table test.ty trx id 462308398 lock_mode X locks gap before rec insert intention waiting **
表示事务2的insert 语句正在等待插入意向锁 lock_mode X locks gap before rec insert intention waiting (LOCK_X + LOCK_REC_GAP )
这里需要各位注意的是锁组合,类似lock_mode X waiting ,lock_mode X,lock_mode X locks gap before rec insert intention waiting 是我们分析死锁的核心重点。

锁组合

锁组合呢?
首先我们要知道对于MySQL有两种常规锁模式

  • LOCK_S(读锁,共享锁)
  • LOCK_X(写锁,排它锁)
    最容易理解的锁模式,读加共享锁,写加排它锁。

锁属性

  • LOCK_REC_NOT_GAP (锁记录)
  • LOCK_GAP (锁记录前的GAP)
  • LOCK_ORDINARY (同时锁记录+记录前的GAP 。传说中的Next Key锁)
  • LOCK_INSERT_INTENTION (插入意向锁,其实是特殊的GAP锁)
    锁的属性可以与锁模式任意组合。例如:
    lock->type_mode 可以是Lock_X 或者Lock_S
    locks gap before rec 表示为gap锁:lock->type_mode & LOCK_GAP
    locks rec but not gap 表示为记录锁,非gap锁:lock->type_mode & LOCK_REC_NOT_GAP
    insert intention 表示为插入意向锁:lock->type_mode & LOCK_INSERT_INTENTION
    waiting 表示锁等待:lock->type_mode & LOCK_WAIT

参考资料