MySQL锁机制#
一句话答案#
InnoDB 锁体系:行锁(记录锁/间隙锁/临键锁)+ 表锁(意向锁/AUTO-INC 锁),行锁基于索引加锁。
核心要点
按锁粒度:
1. 表级锁
LOCK TABLES table READ/WRITE- 意向锁(IS/IX):行锁前先加意向锁,使表锁检测行锁是否存在更高效
- 事务要加行共享锁 → 先在表上加意向共享锁(IS)
- 事务要加行排他锁 → 先在表上加意向排他锁(IX)
2. 行级锁(InnoDB 特有,针对索引项加锁)
| 锁类型 | 英文 | 锁定范围 | 作用 |
|---|---|---|---|
| 记录锁(Record Lock) | Record Lock | 锁定单个索引记录 | 防止其他事务修改/删除该行 |
| 间隙锁(Gap Lock) | Gap Lock | 锁定两个索引记录之间的”间隙”(不含端点) | 防止在间隙中插入新行(防幻读) |
| 临键锁(Next-Key Lock) | Next-Key Lock | 记录锁 + 左侧间隙锁 = 前开后闭区间 (a, b] | InnoDB 默认,同时防止修改和插入幻行 |
| 插入意向锁 | Insert Intention Lock | 插入操作前加,与 Gap Lock 互斥 | 表示准备在间隙中插入 |
3. 按性质
- 共享锁(S锁,读锁):多事务可同时持有,阻塞写
- 排他锁(X锁,写锁):独占,阻塞其他读锁和写锁
Next-Key Lock 示例:
数据:id = 1, 5, 10, 15
索引范围及 Next-Key Lock 区间:
(-∞, 1] (1, 5] (5, 10] (10, 15] (15, +∞)
当前读 WHERE id = 5:
→ 对 (1, 5] 加 Next-Key Lock
→ 防止其他事务插入 id ∈ (1, 5] 的新行(防幻读)
→ 防止其他事务修改 id = 5 的行plaintext加锁是动态推演,不是固定加 Next-Key Lock(RR 级别核心规则):
InnoDB 默认基础单位是 Next-Key Lock(前开后闭),但会根据”索引类型 + 等值/范围 + 是否命中”动态退化或延伸。面试官问”这条 SQL 到底锁了哪些区间”,就是在考这套推演。前提:RR 隔离级别、当前读(
SELECT ... FOR UPDATE / UPDATE / DELETE)。
唯一索引(含主键)等值查询:
- 命中(记录存在)→ 退化为记录锁,只锁这一行,不加间隙锁(因为唯一索引保证不会再插入重复值,无幻读风险,没必要锁间隙)
- 未命中(记录不存在)→ 退化为间隙锁,锁住该值所在的那个间隙,防止别人插进来;不锁记录(本来就没记录)
二级索引(非唯一索引)等值查询:
- 命中 → 对二级索引加 Next-Key Lock(二级索引可重复,必须锁间隙防插入),并且向右扫描到第一个不满足条件的值,对该值加间隙锁
- 同时还要回表对命中行的聚簇索引(主键)加记录锁——否则别人能绕过二级索引、直接按主键改这行,锁不全
范围查询(>、<、>=、BETWEEN 等):
- 从满足条件的第一个区间开始,Next-Key Lock 向右逐个延伸,直到第一个不满足条件的记录所在的区间为止(含该区间,以挡住边界处的插入)
- 例:
id >= 10(数据 1,5,10,15)→ 锁(5,10]、(10,15]、(15,+∞);id > 5 AND id <= 15→ 锁(5,10]、(10,15]
等值推演示例(数据 id=1,5,10,15,主键):
WHERE id = 10 (命中) → 记录锁,只锁 id=10
WHERE id = 7 (未命中) → 间隙锁 (5, 10),不锁任何记录
WHERE id >= 10 → (5,10] + (10,15] + (15,+∞)
WHERE id > 15 → (15, +∞)plaintext记忆口诀:唯一索引等值——命中只锁点、未命中只锁缝;范围查询——临键锁一路向右锁到第一个不满足的为止;二级索引——索引锁 + 回表锁主键,一个都不能少。
面试回答(2分钟版)
InnoDB 锁体系从粒度上分三层。全局锁用 FLUSH TABLES WITH READ LOCK 锁住整个实例,用于全库备份。表级锁包括 LOCK TABLES、元数据锁 MDL 和意向锁,其中意向锁 IS 和 IX 由 InnoDB 自动加,事务加行共享锁前先加 IS,加行排他锁前先加 IX,这样其他事务加表锁时只需检查意向锁就能判断有无行锁冲突。行级锁是 InnoDB 特有的,分三种:记录锁锁单个索引记录防止修改删除,间隙锁锁两个记录之间的间隙防止插入,临键锁是两者组合构成前开后闭区间,是默认行锁算法。按性质分为共享锁 S 允许多事务同时读,排他锁 X 独占阻塞其他锁。特别注意 InnoDB 行锁加在索引上而非数据行,SQL 没走索引时行锁退化为表锁,严重影响并发,所以 UPDATE 和 DELETE 一定要确保走索引。
追问与易错
追问方向:
- “什么情况下行锁升级为表锁?”→ 当 UPDATE/DELETE 的 WHERE 条件没有走索引时,InnoDB 无法定位具体行,退化为对所有行加锁,效果等同于表锁;严格说 InnoDB 不存在锁升级机制,而是索引缺失导致的全行扫描加锁
- “意向锁有什么作用?”→ 意向锁是表级锁,事务加行锁前先在表上加 IS/IX 意向锁;其他事务加表锁时只需检查意向锁即可判断是否有行锁冲突,避免逐行扫描,将表锁冲突检测从 O(n) 降到 O(1)
- “怎么查看当前锁信息?”→ MySQL 8.0 查 performance_schema.data_locks 和 data_lock_waits 表;5.7 查 information_schema.INNODB_LOCKS 和 INNODB_LOCK_WAITS;也可用 SHOW ENGINE INNODB STATUS 查看锁等待和死锁信息
- “这条 SQL 具体锁了哪些区间?(加锁推演)”→ RR 当前读下按规则推:唯一索引等值命中→退化记录锁不加间隙;未命中→退化间隙锁;二级索引等值→索引上加 Next-Key 并向右锁到第一个不满足的间隙,同时回表对主键加记录锁;范围查询→Next-Key Lock 向右延伸到第一个不满足条件的记录所在区间为止
易错点:
- ❌ InnoDB 只有行锁——还有表锁(意向锁/AUTO-INC)
- ❌ 行锁锁的是行——锁的是索引记录