意向锁是表级加锁前的快速判断机制,通过一次元数据检查替代全表行锁扫描,避免大表随机I/O;IS与IX彼此兼容,但均与表级X锁互斥;其自动加锁特性易导致DDL被阻塞,需在RR隔离级下验证。
为什么表级加锁要先查意向锁,而不是直接扫行锁?
因为逐行检查是否已被加锁(比如判断全表有没有 X 锁)在大表上会触发大量随机 I/O,性能崩得比锁等待还快。InnoDB 用意向锁把“表里有没有行被锁”这个判断压缩成一次表级元数据检查——只要看到
锁,就知道底下至少有一行正被修改;看到
锁,就知道有行正被读;两者都无,才可能安全加表级
锁。
不走意向锁路径的代价:对百万行表加
,引擎真会尝试遍历所有聚簇索引页找活跃行锁(实际不会真干,但兼容逻辑上必须防住)
意向锁是自动加的:你执行
,InnoDB 自动在表上加
、在行上加
,无需手动干预
显式加表锁时,意向锁才是第一道闸机:比如
会加
,此时另一个事务想
就立刻被拒,根本不用碰任何数据页
IS 和 IX 锁之间到底“兼容”还是“冲突”?
和
彼此兼容,这是关键设计。否则两个只读事务(都加
)或两个更新事务(都加
)就互相阻塞了,彻底废掉并发能力。
真正冲突的是:表级
锁与任何意向锁(
/
)都互斥——因为表级写锁要求“整张表完全空闲”,不能容忍底下有任何行锁
(行级共享锁)和
兼容:允许读已更新的行(如
与
并存)
(行级排他锁)和
兼容:允许更新已被读的行(如
与
并存)
什么时候会意外触发 IX 锁阻塞表级 DDL?
不是只有
才加
。任何对行施加
锁的操作都会连带加
,包括
、
、显式
,甚至某些唯一键冲突回滚场景也会短暂持有
。
典型卡死链:
正在执行
(未提交),此时
会被阻塞——DDL 需要表级
锁,而
与之冲突
排查方法:查
看
和
,再关联
和
确认是否被
挡住
规避建议:DDL 尽量避开业务高峰;用
替代原生
,它会绕过表级
锁需求
测试 IS/IX 行为时最容易忽略的隔离级别陷阱
在
或更低隔离级别下,
不加任何锁(包括
),所以你测不出
的效果。必须用
或显式加锁语句才能触发意向锁。
错误示范:
→ 表上无
,后续
不会等
正确验证:
→ 此时查
能看到
且表上有
注意:MySQL 8.0+ 中
已废弃,改用
查当前锁状态
意向锁本身不解决死锁,也不提升单条 SQL 速度,它只是让“表级锁能否成立”这个决策从 O(n) 降到 O(1)。真正容易翻车的地方,永远是那个你以为没锁的
,其实悄悄带上了
,然后默默挡住了凌晨三点的 DDL 任务。
IXISXLOCK TABLES t WRITEUPDATE t SET a=1 WHERE id=5IXXSELECT * FROM t LOCK IN SHARE MODEISLOCK TABLES t WRITEISIXISIXXISIXSIXSELECT ... LOCK IN SHARE MODEUPDATEXISUPDATESELECT ... LOCK IN SHARE MODEUPDATEIXXIXINSERTDELETESELECT ... FOR UPDATEIXTRX-AINSERT INTO t VALUES (100)ALTER TABLE t ADD COLUMN c INTXIXinformation_schema.INNODB_TRXTRX_STATETRX_QUERYINNODB_LOCKSINNODB_LOCK_WAITSIXpt-online-schema-changeALTERXREAD COMMITTEDSELECTISISREPEATABLE READSET TRANSACTION ISOLATION LEVEL READ COMMITTED; SELECT * FROM t;ISLOCK TABLES t WRITESTART TRANSACTION; SELECT * FROM t LOCK IN SHARE MODE;INNODB_TRXTRX_ISOLATION_LEVEL = REPEATABLE READISinformation_schema.INNODB_LOCKSperformance_schema.data_locksSELECTIS