主题
概念卡片:MySQL 锁机制
一句话机制
InnoDB 的行锁遵循「两阶段锁协议」——需要时加锁、事务结束才释放;死锁靠主动检测(innodb_deadlock_detect)回滚其一解决,但热点行更新时死锁检测的 O(n²) 开销会吃掉 CPU。 锁的性能优化核心是「减少对同一资源的并发冲突」。
锁的层级
| 锁 | 粒度 | 支持引擎 |
|---|---|---|
| 全局锁 | 整库(FTWRL) | 全库备份用 |
| 表锁 | 整表 | MyISAM/InnoDB 都有 |
| 行锁 | 单行 | 仅 InnoDB(MyISAM 不支持) |
MyISAM 只支持表锁,同一张表任何时刻只有一个更新能执行——这是它并发差的根本原因。
两阶段锁协议
行锁在需要时加上,但直到事务结束(commit)才释放。
→ 推论:事务里要锁多行时,把最可能冲突、最影响并发度的锁尽量往后放,缩短锁持有时间。
例:电影票交易「扣顾客余额 → 加影院余额 → 记日志」,把「加影院余额」(热点行)放最后,最大化减少锁等待。
死锁与死锁检测
死锁 = 事务 A 等 B 的行锁、B 等 A 的行锁,循环等待。两种策略:
| 策略 | 参数 | 问题 |
|---|---|---|
| 等待超时 | innodb_lock_wait_timeout(默认 50s) | 死锁要等 50s,太长;调短误伤普通锁等待 |
| 主动检测 | innodb_deadlock_detect(默认 ON) | 热点行更新时检测开销 O(n²) 吃掉 CPU |
热点行更新的经典故障
1000 个并发线程更新同一行:每个被堵线程都做一次死锁检测(O(n)),总共 100 万量级 → CPU 100%、每秒只执行几十个事务。
三种解法:
- 关死锁检测(确保业务无死锁才敢用,有风险)
- 控制并发度(服务端/中间件把同行的更新排队,不让 1000 个线程同时进引擎)
- 一行拆多行(如影院余额拆 10 条记录,冲突概率降为 1/10;但退票要处理某行归零的情况)
锁的类型补充(结合 hxq 底层原理)
- 共享锁(S)/ 排他锁(X):读锁/写锁
- 间隙锁(Gap Lock):锁住索引记录间的间隙,防幻读
- next-key lock:行锁 + 间隙锁的组合,InnoDB RR 级别防幻读的默认锁
常见误解(避坑)
- ❌ "行锁用完就释放"。→ 两阶段锁协议:事务结束才释放,不是语句结束。
- ❌ "死锁只能靠超时解决"。→ 默认开死锁检测主动回滚,超时反而慢(50s)。
- ❌ "死锁检测没有代价"。→ 热点行更新时它是性能杀手(O(n²)),需控制并发度或拆行。
- ❌ "MySQL 所有引擎都支持行锁"。→ 只有 InnoDB 支持,MyISAM 只有表锁。
关联
- 原始资料:07_行锁功过:怎么减少行锁对性能的影响? · 9.1 锁,不同隔离等级下的加锁方法
- 总览:MySQL技术栈总览
- 相关卡:概念卡片:MySQL事务与MVCC(锁管当前读、MVCC 管快照读)
- 域地图:A00-百科/数据与存储/数据与存储