1259 字
约 4 分钟
9
S11-A01 · 引擎、主键与锁
2024-12-31
2026-11-07

适用读者:要决定"用哪个引擎、主键怎么定、锁冲突怎么排查"的人。
前置:S11-B06。
实测环境:MySQL 8.0.46 / InnoDB(死锁实测,2026-11-05)。
覆盖旧文:858(111) 859(97) 860(86) 2404(377) 841(80) 842(80) = 831 点;认领 S05 移交的 A-2381、S08 移交的死锁内容。
覆盖扩展:E-S11-007(自增不连续)、E-S11-008(锁升级)、E-S11-020(死锁排查 SOP)。
上一篇:S11-J05 · 日志与恢复:redo / undo / binlog
下一篇:S11-A02 · 部署与迁移。


1. 一句话结论

InnoDB 是唯一该考虑的引擎(事务 + 行锁 + 崩溃恢复);自增主键不连续是正常的;死锁不是"错误",是检测器主动回滚——加锁顺序一致就能避免。


2. InnoDB vs MyISAM(A-838)

维度 InnoDB MyISAM
事务 ✅ ❌
锁粒度 行锁 表锁
崩溃恢复 ✅(redo) ❌
外键 ✅ ❌
全文索引 5.6+ 支持 支持

判据:"需要事务吗?" 需要 → InnoDB。MyISAM 在现代业务里基本没有位置(除只读归档)。


3. 自增主键为什么不连续(E-S11-007,A-2404)

原因 实测/说明
delete 不重置 实测:删完再插 → id=4(不是 1)
truncate 才重置 实测:→ id=1
批量插入预分配 一次申请 N 个,用完丢弃
事务回滚 已分配的 id 不会退回
唯一键冲突 失败也消耗 id

"主键不连续是 bug 吗"——不是,它是"性能换连续性"的设计:批量预分配减少锁竞争。

"自增用完了怎么办"(A-841):bigint 上限约 9.2×10^18,实际不会用完;真担心 → 雪花算法/号段(S08-A01)。


4. 锁与死锁(E-S11-008、E-S11-020)

4.1 锁的层次

锁 粒度 说明
行锁 行 基于索引;没走索引会退化为表锁
间隙锁 索引区间 RR 下防幻读
表锁/意向锁 表 DDL、显式 lock tables

"没走索引会锁全表" 是最容易被忽略的(A-860):update t set ... where 无索引列 → 锁住扫描到的所有行。

4.2 死锁实测

两个会话交叉加锁:

-- A: update t_dl set v=v+1 where id=1;  ... update ... where id=2;
-- B: update t_dl set v=v+1 where id=2;  ... update ... where id=1;

实测输出:

ERROR 1213 (40001) at line 1: Deadlock found when trying to get lock;
                              try restarting transaction

SHOW ENGINE INNODB STATUS 的关键段:

LATEST DETECTED DEADLOCK
*** (1) TRANSACTION: ...
*** (1) HOLDS THE LOCK(S):  RECORD LOCKS ... lock_mode X locks rec but not gap
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: ... waiting

读法:持有 A 锁、等 B 锁;对方持有 B 锁、等 A 锁 → 环形等待 → 检测器回滚一侧。

4.3 死锁四条件与破坏

条件 破坏方式
互斥 尽量无锁/乐观锁(A-858)
占有并等待 一次性申请
不可剥夺 设置 innodb_lock_wait_timeout(默认 50s)
循环等待 统一加锁顺序(最实用)

4.4 排查 SOP

① 应用侧:ERROR 1213 → 重试(幂等前提下)
② 数据库侧:SHOW ENGINE INNODB STATUS → LATEST DETECTED DEADLOCK
③ 定位:两个事务的 SQL 与加锁顺序
④ 修复:统一顺序 / 缩短事务 / 补索引(避免锁全表)

5. 为什么不推荐外键(A-842)

原因 说明
性能 每次写入都要检查父表
分库分表后失效 跨库外键不可能
锁扩散 父子表互相加锁,增加死锁概率
迁移困难 改结构受约束顺序限制

实践:外键约束放在应用层(或干脆靠业务保证),数据库只做索引。


6. 五个误区

误区 真相
"自增一定连续" 不连续是常态
"行锁就是锁一行" 没走索引会锁更多行
"死锁是数据库 bug" 是检测器的正常机制
"外键越多越安全" 性能/死锁/分库都受影响
"MyISAM 更快所以能用" 无事务,现代业务不可接受

7. 自检(架构师版)

  1. InnoDB 与 MyISAM 的四点差异?
  2. 自增不连续的五个原因?
  3. 死锁 ERROR 1213 的含义?为什么是"回滚一侧"?
  4. 统一加锁顺序为什么能防死锁?
  5. 不推荐外键的四条理由?

8. 下一篇

S11-A02 · 部署与迁移:Docker 跑 MySQL、Flyway

锁讲完,下一篇讲"怎么把数据库跑起来、怎么安全改结构"。


本篇属于系列《S11 · 数据库与 SQL》。旧文来源:A-858/859/860/2404/841/842(含 S05/S08 移交的死锁内容);扩展点 E-S11-007/008/020。死锁与自增行为为 MySQL 8.0.46 实测。

S11-A01 · 引擎、主键与锁
https://www.clxhxhhr.top/posts/5339/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。