意向锁解决表级加锁时避免全表扫描行锁的问题:不加意向锁时LOCK TABLES需检查每行锁状态,引发大量随机I/O;加意向锁后仅需读取表级元数据,IS允许READ表锁、拒绝WRITE,IX拒绝所有表锁。
不加意向锁时,LOCK TABLES t WRITE 这类语句真会尝试检查每行是否被其他事务加了 X 锁——不是逻辑上必须这么做,而是兼容性兜底逻辑要求它能判断“整张表是否空闲”。对千万行表,这等于触发大量随机 I/O,锁请求本身就会卡住几秒。意向锁把这个问题压缩成一次表级元数据读取:看到 IX 就知道底下肯定有行正被修改,直接拒绝;看到 IS 就知道至少有行被读,WRITE 仍拒绝,但 READ 可放行。
你写不出 LOCK TABLES ... IN INTENTION MODE 这种语句——意向锁完全由 InnoDB 自动加,但它的出现取决于你执行的语句类型:
SELECT ... LOCK IN SHARE MODE → 触发 IS 锁(同时在命中行加 S 锁)INSERT、UPDATE、DELETE、SELECT ... FOR UPDATE → 触发 IX 锁(同时在命中行加 X 锁)UPDATE 或 DELETE → 仍加 IX,但会为全表每行加 X 行锁,效果接近“伪表锁”,此时 IX 是真瓶颈,不是假象容易错判的是:以为 SELECT 不加锁就不会有意向锁——其实在 REPEATABLE READ 隔离级别下,SELECT ... LOCK IN SHARE MODE 才加 IS;普通 SELECT 在 READ COMMITTED 下根本不会触发任何意向锁。
很多人查 performance_schema.data_locks 看到一堆 IS 和 IX 就慌,其实它们彼此完全兼容:IS 和 IX 可以共存,多个只读事务或多个更新事务不会因此互相阻塞。真正起作用的兼容规则是:
IS 允许 LOCK TABLES t READ,但拒绝 LOCK TABLES t WRITE
IX 拒绝所有表级锁:READ 和 WRITE 都会被阻塞IX 与行级 S 锁兼容,IS 与行级 X 锁兼容——这才是高并发能成立的基础DDL(如 ALTER TABLE)被卡住,90% 是因为某个未提交事务持有 IX,而不是因为你写了什么错的 SQL。
data_locks 会漏掉关键信息SELECT object_schema,object_name,lock_type,lock_mode FROM performance_schema.data_locks WHERE lock_type = 'TABLE' 能看到 IS 或 IX,但这只是“症状”。真正要查的是背后那个事务:
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX 看 TRX_STATE 是否为 RUNNING 或 LOCK WAIT,再看 TRX_QUERY 是什么语句TRX_STATE = RUNNING 且 TRX_STARTED 很久,大概率是应用层没 COMMIT 或网络超时挂住了INNODB_LOCK_WAITS 能告诉你谁在等谁,但前提是等待已发生;而 IX 长期存在却无等待,说明问题不在锁本身,而在事务生命周期管理意向锁本身从不阻塞任何操作,它只是个标记。看到它长期存在,别优化 SQL,先查事务有没有 commit。