MYSQL为什么会发生死锁,怎么解决
把死锁想成“两个人同时堵在一条窄巷的两头,谁也过不去”——他们手里各自攥着对方需要的钥匙,又不肯先把自己的钥匙交出来,于是永远僵持。
1️⃣ 窄巷模型(业务场景)
- 窄巷 = 订单表
- 两个人 = 两个并发事务 A、B
- 钥匙 = next-key 锁(记录锁 + 间隙锁)
2️⃣ 死锁四步漫画
时间 | 事务 A(想插 1007) | 事务 B(想插 1008) |
---|---|---|
① | 先 SELECT ... FOR UPDATE 1007 不存在,于是给 (1006, +∞] 贴上“禁止新座位”封条(next-key 锁) | |
② | 同样去 SELECT ... FOR UPDATE 1008 也不存在,也想给 (1006, +∞] 贴封条 → 发现已经被 A 贴过,只能排队等 | |
③ | 现在真的插入 1007,需要再往 (1006, +∞] 里放一把“插入意向锁”的小钥匙 → 发现 B 正拿着同区间封条,也排队等 | |
④ | … | … |
→ 互相等对方先放封条 → 循环等待 → 死锁!
3️⃣ 一句话总结
“两把锁同区间,先查后插互等待,就像两人堵巷口,谁都过不去。”
4️⃣ 怎么拆死锁
- 设置超时:巷子口立个倒计时牌(
innodb_lock_wait_timeout
),到点把一个人请出去。 - 死锁检测:保安巡逻发现循环,直接拉走其中一人(
innodb_deadlock_detect = on
)。 - 业务预防:把 order_no 设成唯一索引,用“唯一冲突”代替“先查后插”,巷子直接修成单行道,根本不会出现两把钥匙。