Skip to content

概念卡片: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%、每秒只执行几十个事务

三种解法:

  1. 关死锁检测(确保业务无死锁才敢用,有风险)
  2. 控制并发度(服务端/中间件把同行的更新排队,不让 1000 个线程同时进引擎)
  3. 一行拆多行(如影院余额拆 10 条记录,冲突概率降为 1/10;但退票要处理某行归零的情况)

锁的类型补充(结合 hxq 底层原理)

  • 共享锁(S)/ 排他锁(X):读锁/写锁
  • 间隙锁(Gap Lock):锁住索引记录间的间隙,防幻读
  • next-key lock:行锁 + 间隙锁的组合,InnoDB RR 级别防幻读的默认锁

常见误解(避坑)

  1. ❌ "行锁用完就释放"。→ 两阶段锁协议:事务结束才释放,不是语句结束。
  2. ❌ "死锁只能靠超时解决"。→ 默认开死锁检测主动回滚,超时反而慢(50s)。
  3. ❌ "死锁检测没有代价"。→ 热点行更新时它是性能杀手(O(n²)),需控制并发度或拆行。
  4. ❌ "MySQL 所有引擎都支持行锁"。→ 只有 InnoDB 支持,MyISAM 只有表锁。

关联

最近更新