跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

如何利用意向锁提高表级加锁效率_IS锁与IX锁的层级传递机制

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

相关文章