MySQL InnoDB 意向锁(IS/IX):原理、兼容性与实战排查
在 InnoDB 中做并发控制时,很多人对行锁(Record Lock)很熟悉,却对表级的意向锁(Intention Lock)一知半解。本文把 IS / IX 意向锁的原理、兼容规则和线上排查方法一次讲清楚。
一、为什么需要意向锁
InnoDB 支持多粒度锁定:行锁和表锁可以同时存在。假设没有意向锁,事务 A 想给整张表加表级排他锁(比如执行 LOCK TABLES t WRITE 或 DDL),为了确认表里没有行被其他事务锁住,它必须逐行扫描检查——表越大越慢。
有了意向锁,规则变成:任何事务在给行加共享 / 排他锁之前,必须先在表级加上对应的意向锁。
- 加行共享锁(S)前,先加表级意向共享锁(IS)
- 加行排他锁(X)前,先加表级意向排他锁(IX)
想锁整张表的事务只需检查表上有没有 IS / IX,一眼就知道有没有行级锁在用,不用扫全表。冲突检测从 O(行数) 降到 O(1),这就是意向锁存在的全部意义。
二、IS 与 IX
| 锁类型 | 全称 | 含义 |
|---|---|---|
| IS | Intention Shared(意向共享) | 事务打算在表的某些行上加共享锁 |
| IX | Intention Exclusive(意向排他) | 事务打算在表的某些行上加排他锁 |
意向锁是表级锁,但它本身不阻塞任何数据的读写,它只是一个“声明”:告诉其他事务,这张表的某些行已经被(或即将被)加上行锁。
三、兼容性速记
意向锁之间互相兼容(IS 与 IS、IS 与 IX、IX 与 IX 都兼容),它只和表级的 S / X 锁做兼容性判断:
| 表级 S | 表级 X | |
|---|---|---|
| IS | 兼容 | 冲突 |
| IX | 冲突 | 冲突 |
一句话记忆:只要有人想对整张表加 X 锁,而表上有任何意向锁(IS 或 IX),就会被阻塞;想对整张表加 S 锁,则只被 IX 阻塞。
注意:意向锁只解决“表锁 vs 行锁”的冲突检测,不解决行与行之间的冲突——那是行锁自己的事。
四、实际例子
-- 会话 A
START TRANSACTION;
SELECT * FROM t WHERE id = 1 FOR UPDATE;
-- InnoDB 自动:先给表 t 加 IX,再给 id=1 这一行加 X 锁
-- 会话 B
LOCK TABLES t WRITE;
-- 需要表级 X 锁,看到表上有 IX,直接阻塞等待,直到 A 提交
-- 会话 C
SELECT * FROM t WHERE id = 2 LOCK IN SHARE MODE;
-- 先给表加 IS,IS 与 IX 在表级兼容,C 可以继续;
-- 真正的读写冲突由行锁机制处理
五、线上排查:DDL 一直卡住
典型现象:一个长事务没提交,ALTER TABLE 或 LOCK TABLES 一直卡住。根因往往不是 DDL 本身慢,而是表上的意向锁挡住了表级锁的申请。
排查步骤:
- 先找长事务:
SELECT * FROM information_schema.innodb_trx\G
- 看表级锁到底是什么(MySQL 8.0):
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_MODE, LOCK_STATUS
FROM performance_schema.data_locks
WHERE OBJECT_SCHEMA = 'yourdb' AND OBJECT_NAME = 't';
注意:意向排他锁在这里的 LOCK_TYPE 是 TABLE,LOCK_MODE 才是 IX——IX 是锁模式,不是锁类型。
- 配合
performance_schema.data_lock_waits看谁在等谁。
六、常见误解
- 「意向锁会阻塞插入」——不会。阻塞插入的是间隙锁(Gap Lock)和插入意向锁(Insert Intention),它们属于行级锁范畴,和表级意向锁不是一回事。
- 「读操作不加锁所以没有意向锁」——普通
SELECT是快照读确实不加锁;但SELECT ... FOR UPDATE/LOCK IN SHARE MODE属于当前读,会加行锁,同时自动带上对应的意向锁。 - 意向锁由 InnoDB 自动添加和释放,没有单独的加锁语法,用户不需要、也不能手动干预。
七、实践建议
- 保持事务尽量短,DDL 前先检查有没有长事务;
- 表结构变更尽量走 Online DDL 或 gh-ost / pt-osc,避免在业务高峰直接执行;
- 排查锁等待时,先分清是表级意向锁(IS/IX)还是元数据锁(MDL)在挡路,两者机制不同,查的视图也不同。
意向锁的设计思想很值得借鉴:用一层廉价的“意图声明”,把昂贵的全局检查变成常数时间的局部判断。理解了它,再看表锁和行锁的共存就豁然开朗了。
- 点赞
- 收藏
- 关注作者
评论(0)