UPDATE语句默认加排他锁(X锁);在InnoDB引擎、可重复读隔离级别下,若WHERE条件命中主键或唯一索引则加记录锁,走普通索引则聚簇索引与二级索引均被加锁,无索引时加临键锁,查不到数据时加间隙锁。

update 语句默认加什么锁?
在 InnoDB 引擎、可重复读(RR)隔离级别下,UPDATE 语句执行时**自动加排他锁(X 锁)**,且锁类型取决于 WHERE 条件是否命中索引:
- WHERE 条件走主键或唯一索引 → 加
Record Lock(记录锁),只锁匹配的那行 - WHERE 条件走普通二级索引 → 先在二级索引上加 X 锁,再回表到聚簇索引(主键)上加 X 锁,共两把锁
- WHERE 条件没走索引(全表扫描)→ 退化为锁整个范围,实际会加多个
Record Lock+Gap Lock,合并成Next-Key Lock - WHERE 条件查不到数据(如
WHERE id = 999但表里最大 id 是 100)→ 不加Record Lock,但会在 (100, +∞) 加Gap Lock
Record Lock 是怎么被触发的?
Record Lock 不是独立存在的锁类型,而是 Next-Key Lock 的“点状退化”形态:当 MySQL 确认 WHERE 条件能**精确定位到某条索引记录**,且该记录存在时,就只锁这一条,不带间隙。
关键判断依据是执行计划中的 key 和 rows:
-
EXPLAIN UPDATE user SET name='a' WHERE id=5;显示type: const、key: PRIMARY、rows: 1→ 触发Record Lock - 如果
WHERE age=5且age是普通索引、且存在多条 age=5 的记录 → 实际加的是多个Record Lock(每条匹配记录一把) - 如果
WHERE age=5但age列无索引 → 不走索引,不会只加Record Lock,而是全表扫描+临键锁
为什么有时候 update 看似“没锁住”,其实是 Gap Lock 在起作用?
常见错觉:“我只 update 了一条 id=5 的记录,为什么插入 id=6 的数据被阻塞了?”——这说明你误判了锁范围。真正被锁住的往往不是那条记录本身,而是它周围的间隙。
UPDATE user SET name='x' WHERE id > 3 AND id → 加的是 <code>Gap Lockon (3,8),即禁止在 (3,8) 插入任何新记录,哪怕 id=4、5、6、7 都不存在-
UPDATE user SET name='x' WHERE id = 5;→ 如果 id=5 存在,加Record Lock;但如果 id=5 不存在,而索引中前一条是 id=3、后一条是 id=8,则加Gap Lockon (3,8) - 间隙锁不可见、不显示在
INFORMATION_SCHEMA.INNODB_TRX中,只能通过SHOW ENGINE INNODB STATUS\G查看lock_mode X locks gap before rec这类提示
如何验证当前 update 加的是 Record Lock 还是 Next-Key Lock?
不能只看 SQL 写法,得结合索引结构和数据分布看执行时的实际行为。最直接的方式是开两个事务做并发测试:
- 事务 A:
UPDATE t SET v=1 WHERE id=5;(先不提交) - 事务 B:
INSERT INTO t(id,v) VALUES(5,1);→ 若被阻塞,说明 A 加了Record Lock(id=5 存在) - 事务 B:
INSERT INTO t(id,v) VALUES(4,1);→ 若也被阻塞,说明 A 实际加了Next-Key Lock(比如条件是WHERE id>=5或索引不唯一) - 用
SELECT * FROM performance_schema.data_locks;(MySQL 8.0+)可查具体 lock_data 值,例如lock_data: 5表示 record,lock_data: 5, 6可能表示 gap 范围
Record Lock 的边界很脆:只要 WHERE 条件稍有模糊(比如用了函数、隐式转换、or 分支、非确定性表达式),InnoDB 就可能放弃精确匹配,转而加更大粒度的锁。别信“我写了等值查询就一定只锁一行”这种直觉。


















