意向锁是innodb自动加的表级预告锁,用于高效协调行锁与表锁:is兼容s锁但与x锁互斥,ix与s、x均互斥;执行lock in share mode加is,for update/update/delete/insert加ix。

MySQL的意向锁(Intention Lock)不参与业务逻辑,也不需要你手动加或查,它只是InnoDB在加行锁前自动打的一个“表级预告”——告诉其他事务:“我马上要在某几行上加S锁或X锁”。没有它,表级锁(比如LOCK TABLES ... WRITE)就得挨个扫描每一行有没有被锁,大表上直接卡死。
意向锁怎么自动触发?
你执行任何会加行锁的操作时,InnoDB就悄悄在表上先加对应的意向锁:
-
SELECT ... LOCK IN SHARE MODE→ 自动加IS锁 -
SELECT ... FOR UPDATE、UPDATE、DELETE→ 自动加IX锁 -
INSERT一般不加行锁(除非唯一键冲突),但也会加IX锁(因为可能触发唯一约束检查)
你用 SHOW ENGINE INNODB STATUS\G 能看到意向锁,但它不会出现在 information_schema.INNODB_TRX 或锁等待图里——它太轻量,只起协调作用。
为什么LOCK TABLES t WRITE会被阻塞?
因为这个语句要加表级排他锁(X),而X与所有意向锁(IS 和 IX)都互斥。只要表上有任意事务正在加行锁(哪怕只锁了一行),就必然持有IS或IX,此时LOCK TABLES就会等。
这比老办法(全表扫描每行是否有行锁)快得多——只需要查一次表元数据里的意向锁状态。
常见错误现象:
- 执行
LOCK TABLES orders WRITE卡住,但SELECT * FROM orders LIMIT 1很快 → 很可能是某个长事务正拿着IX锁没提交 - 用
pt-deadlock-logger看不到意向锁参与死锁 → 正常,意向锁本身不构成死锁环,它只是让表级锁“知难而退”
意向锁和行锁的兼容性关系有哪些?
意向锁之间完全兼容:IS 和 IX 可以共存,多个事务能同时在一张表上准备读或写不同行。
真正起冲突的是它们和表级锁的组合:
-
IS兼容S(表级共享锁),但和X(表级排他锁)互斥 -
IX和S、X都互斥 —— 因为只要有写意图,整张表就不能被其他事务以任何方式锁定
注意:IX 和行级 X 不冲突(它们是同一事务内协作的),但 IX 存在时,其他事务的 LOCK TABLES ... READ(即 S)仍可成功 —— 这点容易误判。
什么时候会意外遇到意向锁瓶颈?
不是锁本身慢,而是它暴露了底层设计问题:
- 批量导入用
INSERT ... SELECT+ 唯一键校验 → 每条插入都触发IX,如果目标表很大且有唯一索引,IX会持续持有直到事务结束,阻塞后续LOCK TABLES或 DDL - 应用层用
SELECT ... FOR UPDATE锁整页(比如没走索引的范围查询)→ 实际加了大量行锁,IX虽然还是一个,但背后行锁释放慢,间接拖长IX生命周期 -
ALTER TABLE在 8.0+ 默认用 inplace,但仍需短暂获取X表锁 → 若此时有长事务持IX,DDL 就卡在“waiting for table metadata lock”
最常被忽略的一点:意向锁不会出现在慢查询日志里,也不会被 performance_schema.data_locks 默认展示(需开启相关instrument)。排查表级锁等待时,得从持有 IX/IS 的事务源头入手,而不是盯着意向锁本身。











