MySQL需要意向锁,是因为没有它时表级锁需逐行扫描判断行锁状态,大表下性能断崖式下跌;意向锁是轻量元数据标记,仅存于表级内存结构中,查一次即可确认是否有行锁意图,避免全表遍历。

MySQL需要意向锁,是因为没有它时,表级锁(比如 LOCK TABLES t WRITE 或 ALTER TABLE)必须逐行扫描所有行锁状态才能判断是否安全加锁——大表上几百万行,直接卡死。
为什么表级锁不能直接跳过意向锁?
表级排他锁(X 锁)要求整张表“无人动过数据行”,但 InnoDB 不可能实时维护每行的锁归属快照。意向锁是轻量元数据标记,只存于表级别内存结构中,查一次就能确认“有没有事务正在或打算加行锁”。跳过它,就得现场遍历所有行锁记录,性能断崖式下跌。
-
LOCK TABLES t WRITE阻塞 ≠ 表被锁死了,大概率只是某事务执行了SELECT ... FOR UPDATE后还没提交,留下一个IX锁 -
SHOW ENGINE INNODB STATUS\G能看到IX或IS,但information_schema.INNODB_TRX里查不到——它不计入事务锁统计,纯属协调信号 - 即使只锁一行(如
UPDATE users SET name='x' WHERE id=1),也会触发IX;没索引的UPDATE会锁全表行,但意向锁仍只有一个IX
IS 和 IX 锁分别在什么 SQL 下自动出现?
你写任何可能引发行锁的语句,InnoDB 就在执行前悄悄加好对应意向锁——完全透明,无法禁用或绕过。
-
SELECT ... LOCK IN SHARE MODE→ 自动加IS锁 -
SELECT ... FOR UPDATE、UPDATE、DELETE、带唯一键冲突检查的INSERT→ 自动加IX锁 - 普通
INSERT(无唯一约束、无触发器)一般不加行锁,但依然加IX——因为“意图”已存在,哪怕最终没锁到具体行
意向锁之间为什么能共存?
因为它们不是业务锁,只是广播“我可能要读/写某些行”的信号。多个事务同时想读不同行(各自加 IS),或一个读一个写(IS + IX),彼此不冲突——真正拦路的是它们和表级锁的兼容规则。
-
IS和IX互相兼容:允许读写并发操作不同行 -
IS兼容表级S锁,但与表级X锁互斥 -
IX与表级S和X都互斥——只要有写意图,整张表就不能被其他事务以任何方式锁定 - 注意:
IX存在时,LOCK TABLES t READ(即S)仍可成功,这点常被误判为“没锁住表”
最易被忽略的一点:意向锁本身从不构成死锁环,pt-deadlock-logger 看不到它参与死锁,但它会让表级锁“知难而退”,把阻塞提前暴露出来——问题不在锁本身,而在长事务持有 IX 不释放,导致 DDL 卡住。


















