MySQL DeadLock

本文详细介绍了MySQL InnoDB的锁类型,如记录锁、间隙锁、插入意向锁和Next-key锁,以及它们在并发操作中的冲突和死锁原因。在RR隔离级别下,通过`for update`和`insert`语句的示例分析了死锁的产生,并提供了死锁的解决方法。建议深入理解插入意向锁的作用和锁兼容性规则,以便更好地避免和解决MySQL中的死锁问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

------------------------
LATEST DETECTED DEADLOCK
------------------------
2021-12-09 06:51:13 7f89cac7a700
*** (1) TRANSACTION:
TRANSACTION 46451826235, ACTIVE 1 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1
MySQL thread id 1867664, OS thread handle 0x7f89d0599700, query id 550365490 10.211.0.0 root update
insert into test_device (create_time, creator, modifier, modify_time, order_id, name) values ('2021-12-09 14:51:12.687', '@test', null, null, '02j2it2341svepb99342', 'iphone13 Pro')
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 22123 page no 4 n bits 88 index `UNIQ_ORDER_ID` of table `testdb`.`test_device` trx id 46451826235 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 11 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 9; hex 393134303039303039; asc 914009009;;
 1: len 4; hex 80000035; asc    5;;

*** (2) TRANSACTION:
TRANSACTION 46451826237, ACTIVE 1 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1
MySQL thread id 1867668, OS thread handle 0x7f89cac7a700, query id 550365495 10.211.0.0 root update
insert into test_device (create_time, creator, modifier, modify_time, order_id, name) values ('2021-12-09 14:51:12.684', '@test', null, null, '02j2it2341svepb99342', 'iphone13 Pro')
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 22123 page no 4 n bits 88 index `UNIQ_ORDER_ID` of table `testdb`.`test_device` trx id 46451826237 lock_mode X locks gap before rec
Record lock, heap no 11 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 9; hex 393134303039303039; asc 914009009;;
 1: len 4; hex 80000035; asc    5;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 22123 page no 4 n bits 88 index `UNIQ_ORDER_ID` of table `testdb`.`test_device` trx id 46451826237 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 11 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 9; hex 393134303039303039; asc 914009009;;
 1: len 4; hex 80000035; asc    5;;

*** WE ROLL BACK TRANSACTION (2)

锁类型

  • 记录锁(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

       事务1中,等待获取插入意向锁:lock_mode X locks gap before rec insert intention waiting;事务2中,持有gap lock: lock_mode X locks gap before rec,等待获取插入意向锁:lock_mode X locks gap before rec insert intention waiting

间隙锁(Gap Locks):锁定尚未存在的记录,gap lock可以共存(co-exist),InnoDB会在第一个不满足查询条件的记录上加gap lock,防止新的满足条件的记录插入

下一个键锁(Next-Key Locks):next-key lock 是 (索引记录上的索引记录锁) + (该索引记录前面的间隙上的锁) 二者的合体

插入意向锁(Insert Intention Locks):该锁的范围是(插入值, 向下的一个索引值)

深入理解插入意向锁:一种特殊的gap lock。使用意向锁,实现了“表锁是否冲突”的快速判断。意向锁就是协调行锁和表锁之间的关系的,或者也可以说,意向锁是协调表上面的读写锁和行上面的读写锁(也就是不同粒度的锁)之间的关系的。

MySQL InnoDB锁介绍及不同SQL语句分别加什么样的锁_溺水三千只取一瓢饮-优快云博客_innodb_locksd

这篇文章很棒!建议研读

锁兼容性

| Lock   | X        | IX         | S          | IS         |
| ------ | -------- | ---------- | ---------- | ---------- |
| **X**  | conflict | conflict   | conflict   | conflict   |
| **IX** | conflict | compatible | conflict   | compatible |
| **S**  | conflict | conflict   | compatible | compatible |
| **IS** | conflict | compatible | compatible | compatible |

死锁原因

        网上看了好多文章,云里雾里的,不辨真伪,所以请对此篇文章也持怀疑态度!        

        RR隔离级别下,select...where order_id=? for update,order_id唯一索引

        02j2it2341svepb99342记录如果存在的话,for update会对改行记录加IX锁,并且只会有一个事务执行,其它事务对该记录执行for update会阻塞;

        02j2it2341svepb99342记录不存在的话(本例所示),for update不会加锁,其它事务也能执行for update操作

        执行insert语句时:

        explain A:

        记录不存在的时候,会加gap lock(最大值,+\infty);gap lock之间是兼容的,多个线程可以同时持有;执行到insert时,会先加插入意向锁,再加排他X锁,但是插入意向锁insert intention lock(当前值,+\infty)和gap lock (最大值,+\infty)冲突,所以导致线程阻塞,等待获取gap lock;事务1等待事务2释放gap lock,事务2又等待事务1释放gap lock,导致死锁发生。 

        explain B:

        记录不存在的时候,不会加锁;但是执行insert,先加IX Lock,再加X Lock,两个事务同时等待对方的插入意向锁IX,导致死锁发生。

Prior to inserting the row, a type of gap lock called an insert intention gap lock is set.

       

解决方法?

其它延伸

  1. 什么时候产生gap lock
  2. mysql的锁类型
  3. select ... for update 加锁过程
    1. 针对唯一索引,不存在记录的情况,到底加不加gap X-lock?
    2. 非唯一索引

非唯一键的情况下,insert N 会申请 LOCK_INSERT_INTENTION lock,找到大于N的第一条记录M,如果M上没有Gap lock,Next-key lock,则插入成功。

否则出现等待M上的锁释放。比如:

select * from test_device where order_id>'02j2it2341svepb99342' for update

插入02j2it2341svepb99341,MySQL查看到02j2it2341svepb99341 的下一个记录02j2it2341svepb99342上有Gap lock,Next-key lock,阻塞insert intention,因此出现等待。

参考:

  1. MySQL死锁案例分析一(先delete,再insert,导致死锁) - 简书 (和本文死锁一样)
  2. select for update 并发insert死锁问题 - 简书 (本文情况一样)
  3. mysql条件查询不存在行,使用for update加锁的分析_wj310298的专栏-优快云博客_for update 查询不到数据
  4. 并发插入引发的死锁问题排查_zhoxing-优快云博客_并发insert死锁
  5. mysql并发insert死锁问题——gap、插入意向锁冲突 - hebaodan的博客 - OSCHINA - 中文开源技术交流社区
  6. 解决死锁之路(终结篇)- 再见死锁 - 开顺 - 博客园
  7. mysql insert锁机制(insert死锁)_varyall的专栏-优快云博客_mysql并发insert死锁
  8. MySQL如何对select,select for update进行加锁_HelloWorld搬运工-优快云博客_mysql select 加锁
  9. select for update 语句深度解析 - mr · 大专栏
  10. MySQL :: MySQL 8.0 Reference Manual :: 15.7.1 InnoDB Locking (权威,好好读下【Locks Set by Different SQL Statements in InnoDB】章节)
  11. 读 MySQL 源码再看 INSERT 加锁流程 - aneasystone's blog
  12. 读MySQL源码再看 insert 加锁流程,一个字:牛_hnjsjsac的博客-优快云博客 (这篇文章将的insert加锁过程,包括迷你锁,latch,隐式锁等概念)
  13. 读 MySQL 源码再看 INSERT 加锁流程 - aneasystone's blog (同上)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值