【计算基础|MySQL15】MySQL 锁(下):锁全表与死锁
上一篇把锁的种类和加锁规则讲清了。这篇解决两个生产上最致命的问题:为什么一条没索引的 UPDATE 能把整张表写卡死 ,以及两个事务好好的为什么会死锁、怎么排查和避免。
死锁这东西,面试爱问,线上更爱出。一篇把它讲透:四个必要条件、五个常见死锁场景、InnoDB 怎么自动检测、七条预防策略。
环境:原理基于 MySQL 8.0 InnoDB,RR 隔离级别。涉及复现的部分来自前两篇实战(MySQL13/16)。
一、UPDATE 无索引为什么锁全表
结论先给:InnoDB 行锁加在索引上。WHERE 没索引 → 执行器全表扫描 → 扫到一行加一把 X 锁 → 虽然不匹配的行 Server 层会过滤,但 RR 下这些锁要到事务提交才释放 → 全表所有行都被锁。
1.1 全表扫描加锁过程
#mermaid-svg-Y2nhGzPb5efhP9up{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Y2nhGzPb5efhP9up .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Y2nhGzPb5efhP9up .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Y2nhGzPb5efhP9up .error-icon{fill:#552222;}#mermaid-svg-Y2nhGzPb5efhP9up .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Y2nhGzPb5efhP9up .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Y2nhGzPb5efhP9up .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Y2nhGzPb5efhP9up .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Y2nhGzPb5efhP9up .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Y2nhGzPb5efhP9up .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Y2nhGzPb5efhP9up .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Y2nhGzPb5efhP9up .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Y2nhGzPb5efhP9up .marker.cross{stroke:#333333;}#mermaid-svg-Y2nhGzPb5efhP9up svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Y2nhGzPb5efhP9up p{margin:0;}#mermaid-svg-Y2nhGzPb5efhP9up .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Y2nhGzPb5efhP9up .cluster-label text{fill:#333;}#mermaid-svg-Y2nhGzPb5efhP9up .cluster-label span{color:#333;}#mermaid-svg-Y2nhGzPb5efhP9up .cluster-label span p{background-color:transparent;}#mermaid-svg-Y2nhGzPb5efhP9up .label text,#mermaid-svg-Y2nhGzPb5efhP9up span{fill:#333;color:#333;}#mermaid-svg-Y2nhGzPb5efhP9up .node rect,#mermaid-svg-Y2nhGzPb5efhP9up .node circle,#mermaid-svg-Y2nhGzPb5efhP9up .node ellipse,#mermaid-svg-Y2nhGzPb5efhP9up .node polygon,#mermaid-svg-Y2nhGzPb5efhP9up .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Y2nhGzPb5efhP9up .rough-node .label text,#mermaid-svg-Y2nhGzPb5efhP9up .node .label text,#mermaid-svg-Y2nhGzPb5efhP9up .image-shape .label,#mermaid-svg-Y2nhGzPb5efhP9up .icon-shape .label{text-anchor:middle;}#mermaid-svg-Y2nhGzPb5efhP9up .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Y2nhGzPb5efhP9up .rough-node .label,#mermaid-svg-Y2nhGzPb5efhP9up .node .label,#mermaid-svg-Y2nhGzPb5efhP9up .image-shape .label,#mermaid-svg-Y2nhGzPb5efhP9up .icon-shape .label{text-align:center;}#mermaid-svg-Y2nhGzPb5efhP9up .node.clickable{cursor:pointer;}#mermaid-svg-Y2nhGzPb5efhP9up .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Y2nhGzPb5efhP9up .arrowheadPath{fill:#333333;}#mermaid-svg-Y2nhGzPb5efhP9up .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Y2nhGzPb5efhP9up .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Y2nhGzPb5efhP9up .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Y2nhGzPb5efhP9up .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Y2nhGzPb5efhP9up .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Y2nhGzPb5efhP9up .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Y2nhGzPb5efhP9up .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Y2nhGzPb5efhP9up .cluster text{fill:#333;}#mermaid-svg-Y2nhGzPb5efhP9up .cluster span{color:#333;}#mermaid-svg-Y2nhGzPb5efhP9up div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Y2nhGzPb5efhP9up .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Y2nhGzPb5efhP9up rect.text{fill:none;stroke-width:0;}#mermaid-svg-Y2nhGzPb5efhP9up .icon-shape,#mermaid-svg-Y2nhGzPb5efhP9up .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Y2nhGzPb5efhP9up .icon-shape p,#mermaid-svg-Y2nhGzPb5efhP9up .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Y2nhGzPb5efhP9up .icon-shape .label rect,#mermaid-svg-Y2nhGzPb5efhP9up .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Y2nhGzPb5efhP9up .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Y2nhGzPb5efhP9up .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Y2nhGzPb5efhP9up :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 不匹配, RC下释放
不匹配, RR下
匹配
扫完所有行
UPDATE t SET val=1 WHERE status='active'
(status 无索引)
Server 层: 全表扫描
逐行交给 InnoDB
扫到一行
InnoDB 给这行加 Next-Key Lock (X)
Server 层判断 status='active'?
RC: semi-consistent read
提前释放这行锁
RR: 锁不释放, 持有到事务结束
保留 X 锁, 更新数据
结果: RR下全表所有行都被X锁
其他事务任何写都阻塞
1.2 两个细节
- RC 下有 semi-consistent read:InnoDB 读到不匹配的行,可以提前把锁释放掉再继续扫(优化)。所以 RC 下无索引 UPDATE 锁的范围比 RR 小。
- RR 下不释放:不匹配行的锁要等事务 COMMIT 才释放。这就是为什么"同样一条 SQL,RC 没事、RR 把库锁了"。
🔴 上一篇实战 4 拍的 data_locks 就是铁证:status 无索引时,10、20、30、40 四条记录全被 X 锁;加了 idx_status 后,锁只落在 secondary index 匹配行上。
工程铁律 :大表上任何 UPDATE/DELETE,先 EXPLAIN 看 type 列是不是 ALL(全表扫描),是就停下来加索引,别直接上线。
二、间隙锁到底能不能防幻读
结论:能,但分场景。
| 读方式 | 防幻读靠什么 | 有没有幻读 |
|---|---|---|
| 快照读(普通 SELECT) | MVCC 的 ReadView 快照 | ❌ 没有 |
| 当前读(FOR UPDATE / UPDATE / DELETE) | 间隙锁把范围锁死,别的事务插不进来 | ❌ 没有(RR 下) |
2.1 间隙锁怎么挡住插入
举例:索引上有 10、20、30。A 执行 SELECT * FROM t WHERE id>20 FOR UPDATE,InnoDB 给它加 Next-Key Lock:
text
锁住: (20, 30] 和 (30, +∞)
B 想插 id=25?落在 (20,30] 间隙,被挡。想插 id=99?落在 (30,+∞),被挡。A 事务不提交,B 一条都插不进去------自然就不会有"同一范围两次读行数不一样"的幻读。
2.2 RC 下为什么挡不住
RC 隔离级别没有间隙锁 (外键和唯一键检查除外)。所以 RC 下做 WHERE id>20 FOR UPDATE,只锁 id=30 这条记录本身,不锁间隙。B 想插 id=25 随便插------于是 RC 的当前读会幻读。这是用 RC 换并发必须接受的代价。
⚠️ 一句话:MVCC 用快照解决快照读幻读,间隙锁用物理封锁解决当前读幻读,两套机制各管一摊。
三、死锁的四个必要条件
和操作系统里的死锁一模一样,四个条件同时成立才会死锁:
| 条件 | 含义 | MySQL 里对应什么 |
|---|---|---|
| 1. 互斥 | 资源同一时刻只能一个事务占 | 行锁/间隙锁是排他的 |
| 2. 占有并等待 | 拿着已有的锁,同时等新锁 | A 拿着 id=1 等 id=2 |
| 3. 不可剥夺 | 锁不能被强行抢走 | InnoDB 锁只能等持有者释放 |
| 4. 循环等待 | 形成等待环 A→B→A | A 等 B 的锁,B 等 A 的锁 |
🔴 破坏任意一个条件就能预防死锁。下面的策略基本都是冲着破坏"循环等待"去的。
四、五个常见死锁场景
4.1 交叉加锁(最经典)
事务 A 锁 id=1 再去锁 id=2,事务 B 锁 id=2 再去锁 id=1。上一篇实战 5 完整复现过,WE ROLL BACK TRANSACTION (2)。
4.2 不同索引更新同一批行
两个事务用不同索引更新同一批数据,加锁顺序不同。比如:事务 A 走主键更新,事务 B 走二级索引更新同一行,回表时加锁顺序交叉。
4.3 间隙锁 + 插入(最隐蔽)
上一篇实战 6:A、B 同时锁住 (20,30) 间隙(间隙锁不互斥),然后各自往里插数据------插入意向锁要等对方的间隙锁,互相等,死锁。两个事务操作的主键不同,却死锁了,就是这个原因。
4.4 DELETE + INSERT 唯一键冲突
一个事务 DELETE 了某行但没提交,另一个事务 INSERT 同样的唯一键------要等 DELETE 的锁;同时 DELETE 那边可能又在等别的锁,形成环。
4.5 表级锁 + 行级锁混合
先 LOCK TABLES 拿表锁,事务里又加行锁,和别的只拿行锁的事务顺序交叉。
五、死锁检测与处理
5.1 InnoDB 怎么发现死锁
#mermaid-svg-wQrme1Rchr7fy9By{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wQrme1Rchr7fy9By .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wQrme1Rchr7fy9By .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wQrme1Rchr7fy9By .error-icon{fill:#552222;}#mermaid-svg-wQrme1Rchr7fy9By .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wQrme1Rchr7fy9By .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wQrme1Rchr7fy9By .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wQrme1Rchr7fy9By .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wQrme1Rchr7fy9By .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wQrme1Rchr7fy9By .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wQrme1Rchr7fy9By .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wQrme1Rchr7fy9By .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wQrme1Rchr7fy9By .marker.cross{stroke:#333333;}#mermaid-svg-wQrme1Rchr7fy9By svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wQrme1Rchr7fy9By p{margin:0;}#mermaid-svg-wQrme1Rchr7fy9By .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wQrme1Rchr7fy9By .cluster-label text{fill:#333;}#mermaid-svg-wQrme1Rchr7fy9By .cluster-label span{color:#333;}#mermaid-svg-wQrme1Rchr7fy9By .cluster-label span p{background-color:transparent;}#mermaid-svg-wQrme1Rchr7fy9By .label text,#mermaid-svg-wQrme1Rchr7fy9By span{fill:#333;color:#333;}#mermaid-svg-wQrme1Rchr7fy9By .node rect,#mermaid-svg-wQrme1Rchr7fy9By .node circle,#mermaid-svg-wQrme1Rchr7fy9By .node ellipse,#mermaid-svg-wQrme1Rchr7fy9By .node polygon,#mermaid-svg-wQrme1Rchr7fy9By .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wQrme1Rchr7fy9By .rough-node .label text,#mermaid-svg-wQrme1Rchr7fy9By .node .label text,#mermaid-svg-wQrme1Rchr7fy9By .image-shape .label,#mermaid-svg-wQrme1Rchr7fy9By .icon-shape .label{text-anchor:middle;}#mermaid-svg-wQrme1Rchr7fy9By .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wQrme1Rchr7fy9By .rough-node .label,#mermaid-svg-wQrme1Rchr7fy9By .node .label,#mermaid-svg-wQrme1Rchr7fy9By .image-shape .label,#mermaid-svg-wQrme1Rchr7fy9By .icon-shape .label{text-align:center;}#mermaid-svg-wQrme1Rchr7fy9By .node.clickable{cursor:pointer;}#mermaid-svg-wQrme1Rchr7fy9By .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wQrme1Rchr7fy9By .arrowheadPath{fill:#333333;}#mermaid-svg-wQrme1Rchr7fy9By .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wQrme1Rchr7fy9By .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wQrme1Rchr7fy9By .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wQrme1Rchr7fy9By .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wQrme1Rchr7fy9By .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wQrme1Rchr7fy9By .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wQrme1Rchr7fy9By .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wQrme1Rchr7fy9By .cluster text{fill:#333;}#mermaid-svg-wQrme1Rchr7fy9By .cluster span{color:#333;}#mermaid-svg-wQrme1Rchr7fy9By div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wQrme1Rchr7fy9By .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wQrme1Rchr7fy9By rect.text{fill:none;stroke-width:0;}#mermaid-svg-wQrme1Rchr7fy9By .icon-shape,#mermaid-svg-wQrme1Rchr7fy9By .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wQrme1Rchr7fy9By .icon-shape p,#mermaid-svg-wQrme1Rchr7fy9By .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wQrme1Rchr7fy9By .icon-shape .label rect,#mermaid-svg-wQrme1Rchr7fy9By .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wQrme1Rchr7fy9By .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wQrme1Rchr7fy9By .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wQrme1Rchr7fy9By :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
事务A等事务B的锁
画等待图
A -> B
事务B等事务C的锁
事务C等事务A的锁
检测到环 A→B→C→A?
算每个事务undo量
回滚 undo 最小的事务
报错 1213
继续等待, 直到锁释放或超时
innodb_deadlock_detect=ON(默认):等待图找环,发现即回滚代价小的事务。innodb_lock_wait_timeout=50(秒):不是死锁检测,是锁等待超时------等不到锁 50 秒报 1205。
5.2 看死锁日志
sql
SHOW ENGINE INNODB STATUS\G -- 最近一次死锁, 找 LATEST DETECTED DEADLOCK
SET GLOBAL innodb_print_all_deadlocks=ON; -- 所有死锁写 error log
日志里重点看三段:每个事务正在执行的 SQL 、HOLDS THE LOCK(S) (拿着什么锁)、WAITING FOR THIS LOCK (等什么锁),最后 WE ROLL BACK TRANSACTION (n) 告诉你回滚了谁。上一篇实战 5、6 有完整逐行解读。
六、锁等待 vs 死锁(再强调一次)
| 锁等待 | 死锁 | |
|---|---|---|
| 有没有环 | 没有,只是排队 | 有等待环 |
| MySQL 动作 | 干等,直到超时 1205 | 立刻检测,回滚一个 1213 |
| 错误码 | 1205 | 1213 |
| 业务处理 | 重试/优化索引 | 捕获异常重试整个事务 |
七、死锁预防策略(七条)
- 固定加锁顺序:所有事务按同一顺序访问行(比如都按 id 从小到大),破坏"循环等待"。这是最有效的一条。
- 大事务拆小:事务越大、持锁越久、范围越广,死锁概率越高。一次操作别裹太多行。
- 为查询建合适索引:避免全表扫描锁全表(见第一节),缩小锁范围。
- 降低隔离级别:RC 没有间隙锁,能消除一大类间隙锁死锁------代价是当前读可能幻读,权衡着用。
- 乐观锁代替悲观锁:读多写少的场景用版本号 CAS,不加行锁,自然没行锁死锁。
- 批量操作分批 :
WHERE id BETWEEN ... LIMIT 1000循环跑,别一次锁几十万行。 - 合理设
innodb_lock_wait_timeout:线上设小一点(比如 5s),让卡死的请求早点失败重试,别占着连接。
八、热点行更新:秒杀场景
秒杀就是同一个商品行被成千上万请求同时 UPDATE stock=stock-1------行锁竞争激烈,排队严重。常见解法:
| 方案 | 思路 |
|---|---|
| 热点行拆分 | 一个库存拆成 N 行(stock_0...stock_N),请求随机路由到一行,分散锁竞争 |
| Redis 预减 | 库存放 Redis,Lua 原子扣减,MySQL 异步落库 |
| 队列削峰 | 请求先进 MQ,消费端串行扣减,把并发变串行 |
| 乐观锁 | UPDATE ... SET stock=stock-1 WHERE id=? AND stock>0,失败重试 |
九、面试高频
- 无索引为什么锁全表? 行锁加在索引上,没索引就全表扫描逐行加 X 锁,RR 下不匹配行的锁到事务结束才释放。
- 间隙锁怎么防幻读? 把索引记录间的开区间锁住,别的事务插不进该间隙;快照读则靠 MVCC 快照。
- 死锁四个条件? 互斥、占有并等待、不可剥夺、循环等待。
- 常见死锁场景? 交叉加锁、不同索引更新同行、间隙锁+插入、DELETE/INSERT 唯一键冲突、表锁行锁混合。
- InnoDB 怎么处理死锁? 等待图找环,回滚 undo 量最小的事务,报 1213。
- 怎么避免死锁? 固定加锁顺序、拆小事务、建索引、降隔离级别、乐观锁、分批操作。
- 锁等待和死锁区别? 锁等待无环、等超时报 1205;死锁有环、InnoDB 主动回滚报 1213。
总结:你真正需要记住的 6 件事
- 无索引 UPDATE 全表扫描逐行加 X 锁,RR 下不匹配行的锁不释放 = 锁全表;上线前必看 explain。
- 防幻读是两套机制:快照读靠 MVCC 快照,当前读靠间隙锁物理封锁;RC 没间隙锁所以当前读会幻读。
- 死锁四条件:互斥、占有并等待、不可剥夺、循环等待;预防的核心是破坏循环等待(固定加锁顺序)。
- 最隐蔽的死锁是间隙锁+插入意向锁------两个事务锁同一间隙不互斥,但插入要互相等。
- InnoDB 自动检测死锁(等待图),回滚 undo 小的事务报 1213;锁等待是等超时报 1205,别混。
- 代码必须捕获 1213/1205 并重试事务;热点行用拆分/Redis/队列削峰。
验证清单
- 能画出无索引 UPDATE 的全表扫描加锁流程图
- 能说清 RC 的 semi-consistent read 和 RR 的区别
- 能默写死锁四个必要条件
- 能说出至少三个常见死锁场景,并解释间隙锁+插入为什么死锁
- 能区分 1205 和 1213 两个错误码
- 能说出七条死锁预防策略里至少四条
- 知道秒杀热点行竞争的三种解法
参考资源
- MySQL 8.0 官方文档:InnoDB 死锁 https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlocks.html
- MySQL 8.0:InnoDB 锁等待与死锁检测 https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlock-detection.html
- MySQL 8.0:InnoDB 锁模式(记录/间隙/Next-Key) https://dev.mysql.com/doc/refman/8.0/en/innodb-lock-modes.html
- MySQL 8.0:innodb_lock_wait_timeout https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_lock_wait_timeout
下一篇《【工程实战】行锁/间隙锁/死锁复现排查》:把这两篇原理全部回到 Docker MySQL 8 上动手------用 data_locks 拍下记录锁/间隙锁/Next-Key 的真实 LOCK_MODE、复现无索引锁全表前后对比、构造交叉死锁和间隙锁死锁并逐行读 SHOW ENGINE INNODB STATUS 报告,最后给一份线上锁问题排查 SOP。