意向锁是InnoDB自动加的表级标记,用于快速判断表中是否有行被加锁;IS锁对应共享锁请求,IX锁对应排他锁请求,二者兼容但均与表级X/S锁冲突。

意向锁不是你手动加的,而是InnoDB在执行DML或显式加锁语句时自动加的表级标记;它不解决行锁冲突,只让表锁能秒判“这表里有没有人正在动数据行”。
为什么SELECT FOR UPDATE会触发IX而不是IS
因为FOR UPDATE本质是为后续可能的修改做准备,InnoDB按规则:只要语句带写意图(哪怕最终只锁一行),就先加IX。同理,UPDATE、DELETE、INSERT也一律走IX;只有SELECT ... LOCK IN SHARE MODE才加IS。
常见错误现象:
- 你以为SELECT * FROM t WHERE id = 1 FOR UPDATE只锁一行,结果发现另一个事务执行ALTER TABLE t被卡住——其实是IX与ALTER需要的表级X锁不兼容。
- 用LOCK TABLES t WRITE对InnoDB表强制加锁,会隐式升级为表级X,立刻和所有活跃的IX冲突,报错或无限等待。
- 参数差异:
IS允许其他事务再加IS或IX,但阻塞表级X;IX允许其他事务加IS,但阻塞所有表级S/X - 性能影响:没有意向锁时,
ALTER TABLE要逐行扫描是否被锁;有IX后,只需查一次performance_schema.data_locks里的表级锁记录
如何查当前表上的IS/IX锁是否存在
直接查performance_schema.data_locks视图,过滤lock_type = 'TABLE'且lock_mode含IS或IX:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_MODE, LOCK_DATA FROM performance_schema.data_locks WHERE LOCK_TYPE = 'TABLE' AND LOCK_MODE REGEXP '^(IS|IX)$';
注意:LOCK_DATA字段对表级意向锁恒为NULL,别指望它返回行信息;真正锁住的行在另一条lock_type = 'RECORD'的记录里。
- 使用场景:线上出现
ALTER TABLE长时间卡住,优先查这个视图确认是否有未提交事务残留IX - 容易踩的坑:MySQL 5.7默认关闭
performance_schema,需启动时加--performance-schema=ON;8.0+默认开启但data_locks表需PROCESS权限
IS/IX之间为什么兼容,却都和表级X锁冲突
意向锁本质是“声明”而非“占有”:IS说“我打算读几行”,IX说“我打算改几行”,它们彼此不矛盾,所以兼容;但表级X锁意味着“整张表归我独占”,和任何“有人想动里面某几行”的声明天然冲突。
兼容矩阵的关键逻辑:
- IS ↔ X:冲突(有人想全表写,你就不能声明“我要读其中几行”)
- IX ↔ X:冲突(有人想全表写,你就不能声明“我要改其中几行”)
- IX ↔ S:也冲突(表级S允许并发读,但IX代表后续可能写,破坏一致性)
- 行锁和意向锁是两套系统:行级
X锁之间不互斥(只要不同行),但IX作为表级标记,只管“有没有人在动行”,不管具体哪几行 - 死锁常见于混合场景:长事务持着
IX未提交,同时另一个事务试图LOCK TABLES ... WRITE或TRUNCATE TABLE,双方僵住
最易被忽略的点:意向锁由InnoDB全自动管理,你无法UNLOCK它,也无法绕过它——只要事务没提交,IX就一直挂着,哪怕你只执行了一条SELECT ... FOR UPDATE后就发呆。排查锁问题时,永远先看事务是否异常长驻。


















