Fork me on GitHub

RR隔离级别下的gap和插入意向锁死锁分析

死锁日志的查看

https://segmentfault.com/a/1190000018730103

sql

1
show engine innodb status;

记录锁,间隙锁,Next-key 锁和插入意向锁。这四种锁对应的死锁如下:

记录锁(LOCK_REC_NOT_GAP): lock_mode X locks rec but not gap
间隙锁(LOCK_GAP): lock_mode X locks gap before rec
Next-key 锁(LOCK_ORNIDARY): lock_mode X
插入意向锁(LOCK_INSERT_INTENTION): lock_mode X locks gap before rec insert intention

死锁案例

https://my.oschina.net/hebaodan/blog/1835966
https://my.oschina.net/hebaodan/blog/3033276

gap锁 + 并发insert死锁

表及Sql

image-20220701102753626

分析

image-20220701102803796

锁冲突矩阵

image-20220701102814013

并发delete + insert唯一键冲突死锁

表及场景sql:

image-20220701102823672

分析

image-20220701102834314

死锁日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
*** (1) TRANSACTION:
TRANSACTION 11074, ACTIVE 11 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 10, OS thread handle 123145442168832, query id 160 localhost 127.0.0.1 root updating
// T2的SQL
delete from t4 where a = 1
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: // 等待的锁
RECORD LOCKS space id 68 page no 3 n bits 72 index PRIMARY of table
// 可以看到这里是阻塞等待X行锁
`aliyun`.`t4` trx id 11074 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
0: len 4; hex 80000001; asc ;;
1: len 6; hex 000000002b41; asc +A;;
2: len 7; hex 2e000001dc13c4; asc . ;;

*** (2) TRANSACTION:
TRANSACTION 11073, ACTIVE 40 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 9, OS thread handle 123145442725888, query id 161 localhost 127.0.0.1 root update
// 事务T1的SQL
insert into t4 values(1)
*** (2) HOLDS THE LOCK(S):// 持有的锁
RECORD LOCKS space id 68 page no 3 n bits 72 index PRIMARY of table
`aliyun`.`t4` trx id 11073 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
0: len 4; hex 80000001; asc ;;
1: len 6; hex 000000002b41; asc +A;;
2: len 7; hex 2e000001dc13c4; asc . ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED: // 等待的锁
RECORD LOCKS space id 68 page no 3 n bits 72 index PRIMARY of table
// 这里看到事务T1 S-Next锁 等待行锁
`aliyun`.`t4` trx id 11073 lock mode S waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
0: len 4; hex 80000001; asc ;;
1: len 6; hex 000000002b41; asc +A;;
2: len 7; hex 2e000001dc13c4; asc . ;;

*** WE ROLL BACK TRANSACTION (1)

sql加锁分析

https://www.aneasystone.com/archives/2017/12/solving-dead-locks-three.html (这是一个系列)

基本加锁规则

  • 常见语句的加锁
    • select 快照读,在RC和RR下不加锁
    • select … lock in share mode为当前读,加S锁
    • select … for update为当前读,加X锁
    • dml语句,当前读,加X锁
    • ddl语句,加标记所,隐式提交,不能回滚。
  • 表锁
    • 表锁(S和X锁)
    • 意向锁(IS锁和IX锁)
    • 自增锁(一般看不见 只有在innodb_autoinc_lock_mode=0或者bulk inserts时才可能有)
  • 行锁
    • 记录锁(S和X锁)
    • 间隙锁(S和X锁)
    • Next-Key锁(S和X锁)
    • 插入意向锁
  • 行锁分析
    • 行锁是加在索引上的,最终都会落在聚簇索引上。
    • 加行锁是一行行加的
  • 锁冲突
    image-20220701102847454

  • 不同隔离级别下的锁

    • select快照读在Serializable隔离级别下为当前读,加S锁。
    • RC下没有间隙锁和Next-Key锁(唯一索引情况下有特殊,purge线程+记录有锁唯一索引,会加S Next-Key锁)

对于insert操作

如果没有这两个问题
(1)为了防止幻读,如果记录之间加有 GAP 锁,此时不能 INSERT;(插入意向锁解决)
(2)如果 INSERT 的记录和已有记录造成唯一键冲突,此时不能 INSERT;(对已存在唯一记录加S Next锁)

  • insert一开始只有隐式锁,除非隐式锁转换为显式锁: InnoDb 在插入记录时,是不加锁的。如果事务 A 插入记录且未提交。
    如果这时事务 B 尝试对这条记录加锁,事务 B 会先去判断记录上保存的事务 id 是否活跃,如果活跃的话,那么就帮助事务 A 去建立一个锁对象,然后自身进入等待事务 A 状态,这就是所谓的隐式锁转换为显式锁。还比如对于辅助索引也是隐式锁。
  • insert 对唯一索引的加锁逻辑:
    • 1.先做UK冲突检测,如果存在目标行,先对目标行加S Next Key Lock(该记录在等待期间被其他事务删除,此锁被同时删除)
    • 2.如果1成功,对对应行加插入意向锁
    • 3.如果2成功,插入记录,并对记录加X + 行锁(有可能是隐式锁)

image-20220701102856707

-------------本文结束感谢您的阅读-------------

本文标题:RR隔离级别下的gap和插入意向锁死锁分析

文章作者:夸克

发布时间:2020年09月18日 - 10:09

最后更新:2022年07月01日 - 10:07

原始链接:https://zhanglijun1217.github.io/2020/09/18/RR隔离级别下的gap和插入意向锁死锁分析/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。