MySQL 8.0Notes

第 20 章:锁系统

zjc 于 2026-01-20 发布

这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 InnoDB 锁系统管理表锁、索引记录锁、间隙锁、next-key 锁和插入意向锁,与隔离级别、索引结构和事务顺序密切相关。

20.1 锁层次

Server MDL
  -> InnoDB table lock/intention lock
     -> index record lock
        |-- gap lock
        |-- next-key lock
        +-- insert intention lock

行锁加在索引记录上。没有可用索引时,扫描范围可能扩大,导致锁住大量记录或间隙。

20.2 锁模式

模式 说明
S 共享
X 排他
IS 意向共享
IX 意向排他
AI 自增锁

兼容性遵循 S/S 兼容,X 与其他基本冲突,意向锁用于表级快速判断。

20.3 锁类型

类型 说明
record lock 锁索引记录
gap lock 锁记录前间隙
next-key lock record + gap
insert intention 插入前等待间隙冲突
auto-inc lock 自增分配

RR 下范围扫描可能使用 gap/next-key 防止幻读;RC 下间隙锁使用范围明显缩小。

20.4 加锁示例

CREATE TABLE t(
  id BIGINT PRIMARY KEY,
  a INT,
  KEY idx_a(a)
) ENGINE=InnoDB;

BEGIN;
SELECT * FROM t WHERE a BETWEEN 10 AND 20 FOR UPDATE;

在 RR 下可能锁定:

idx_a 中满足条件的记录及间隙

其他事务在对应间隙插入会被阻塞。

20.5 锁等待

SELECT * FROM information_schema.innodb_trx;
SELECT * FROM performance_schema.data_locks;
SELECT * FROM performance_schema.data_lock_waits;
SELECT * FROM sys.innodb_lock_waits;

排查顺序:

  1. 找 waiting thread;
  2. 找 blocking thread;
  3. 查看锁对象和模式;
  4. 查看 SQL 和事务开始时间;
  5. 判断是否长事务;
  6. 决定等待、回滚或 kill;
  7. 优化索引和访问模式。

20.6 死锁

trx A holds row1 waits row2
trx B holds row2 waits row1

处理:

  1. 检测等待环;
  2. 选择回滚代价较小事务;
  3. 写入死锁日志;
  4. 另一事务继续。
SHOW VARIABLES LIKE 'innodb_print_all_deadlocks';

减少死锁:

  1. 表和行按相同顺序访问;
  2. 缩短事务;
  3. 索引精确;
  4. 避免锁后再等待外部资源;
  5. 重试幂等操作;
  6. 控制并发批处理。

20.7 自增锁

SHOW VARIABLES LIKE 'innodb_autoinc_lock_mode';
模式 特点
0 传统模式
1 连续,简单插入更轻量
2 交错,性能高,语句序列可能不连续

主从复制和 binlog 格式会影响可接受模式。

20.8 源码入口

文件 职责
lock0lock.cc 锁系统
lock0priv.h 内部结构
row0sel.cc 读取加锁路径
row0ins.cc 插入冲突

断点:

b lock_rec_lock
b lock_deadlock_occurs

本章小结

InnoDB 行锁绑定索引记录,隔离级别和访问路径共同决定锁范围。排查锁要看等待关系、事务、索引和 SQL;优化锁要缩短事务、精确索引并统一访问顺序。

思考题

  1. 为什么没有索引会扩大锁范围?
  2. gap lock 和 next-key lock 的区别是什么?
  3. RR 和 RC 下间隙锁行为有什么差异?
  4. 如何定位 blocking thread?
  5. 死锁的常见预防方法有哪些?