意向锁本身不竞争,真正卡住的是它背后那把行锁或间隙锁;ix等意向锁仅是轻量声明,实际阻塞源于后续行锁、间隙锁或插入意向锁的冲突,需透过锁日志定位真实持有者。

意向锁本身不竞争,真正卡住的是它背后那把行锁或间隙锁
MySQL 5.7 的 INTENTION 类锁(如 IX、IS)只是“声明”,本身极轻量、几乎不阻塞。你看到的“意向锁竞争”,实际是事务在获取 IX 后,紧接着申请具体行锁或间隙锁失败,被挂起——而等待日志里显示的 “waiting for lock” 往往只打印了最外层的 IX,掩盖了底层真正的争用点。
为什么 SHOW ENGINE INNODB STATUS 里总看到 IX 锁在等?
这是 InnoDB 日志的简化展示习惯:IX 是事务开始时最先加的锁,后续所有行锁/间隙锁都依附于它。当事务 A 持有某页的 X 行锁,事务 B 尝试对同一页加 IX(为后续 UPDATE 做准备),B 就会因无法与 A 的 X 兼容而卡在 IX 等待上——但真正瓶颈是 A 持有的那把 X 锁没释放。
- 查
TRANSACTIONS段时,重点看lock struct(s)后面的地址和holds行,找谁在 holdX或REC_NOT_GAP锁 - 不要只盯
waiting for this lock to be granted那行,它常是“表象” - 配合
SELECT * FROM performance_schema.data_locks;查当前所有锁的粒度和对象
哪些操作会让 IX 锁暴露真实竞争?
以下场景中,IX 只是“导火索”,背后全是行级资源争抢:
-
INSERT INTO t VALUES (1, 'a'):先加IX,再尝试在主键索引插入位置加INSERT_INTENTION间隙锁 → 若多个事务插同一间隙(如自增主键已到 100,都插 101),就会在间隙上互等 -
UPDATE t SET v = 2 WHERE id = 100(id 是主键):加IX后立即申请X记录锁 → 若事务 A 正在更新 id=100,事务 B 就卡在IX等待,实则是等那把X -
SELECT * FROM t WHERE status = 'pending' FOR UPDATE(status 无索引):加IX后全表扫描,逐行加X→ 其他事务任何写操作都会被阻塞,日志里却只显示 “waiting on IX”
怎么确认不是 IX 本身的问题?
真正在意锁层面做压力测试的人极少。绝大多数所谓“IX 竞争”,其实是索引设计或事务行为导致的连锁反应:
- 运行
EXPLAIN FORMAT=TREE看执行计划:如果type是ALL或index,说明 WHERE 没走索引,IX后面跟着的是全表行锁风暴 - 检查
information_schema.INNODB_TRX中trx_state = 'LOCK WAIT'的事务,对比它们的trx_started和trx_wait_started,时间差大说明锁持有太久,不是锁类型问题 - 临时关闭间隙锁验证:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED,再压测 —— 如果等待大幅减少,说明原罪是 RR 级别下的NEXT-KEY锁,不是IX
真正要盯的从来不是 IX,而是它后面那个没及时释放的 X、那个不该存在的 INSERT_INTENTION、或者那个因缺失索引而被迫扫全表的执行路径。锁等待日志里的 IX,只是数据库在告诉你:“这儿有堵车,但路口红灯不在它身上。”











