rc级别下“行锁升级”本质是锁范围失控,并非innodb主动升级;因索引缺失导致全表扫描加x行锁,或唯一键冲突触发next-key lock,使实际加锁远超预期,效果等同表锁。

RC级别下“行锁升级”本质是锁范围失控,不是InnoDB主动升级
MySQL InnoDB 在 READ COMMITTED(RC)隔离级别下没有真正的锁升级机制——它不会像 SQL Server 那样基于行数阈值自动把 5000 个行锁合并成一个表锁。所谓“行锁变表锁”,其实是查询执行过程中因索引缺失、扫描膨胀或间隙锁残留,导致实际加锁范围远超预期,最终效果等同于表级阻塞。
WHERE条件没走索引:全表扫描触发隐式锁扩散
这是最常见也最容易被忽略的诱因。即使在 RC 下,UPDATE 或 DELETE 的 WHERE 子句若无法命中索引,InnoDB 只能逐行扫描并判断是否匹配;为保证语句执行期间数据不被并发修改,它会对所有扫描过的行加 X 行锁。当扫描比例高(实测常超 20%~30%),事务持有大量分散行锁,其他事务更新任意未被扫描的行也可能被阻塞(尤其配合 SELECT FOR UPDATE 后续操作时),监控上就表现为 TRX_ROWS_LOCKED 异常高、heap size 超 1MB。
- 典型错误:
UPDATE users SET status = 1 WHERE name LIKE '%张%',而name列无索引 - 优化方向:添加
INDEX idx_name(name),或改用前缀匹配(如name LIKE '张%')让索引生效 - 注意:函数包装字段(如
WHERE YEAR(create_time) = 2025)同样导致索引失效
唯一键冲突引发 NEXT-KEY LOCK:RC 下仍存在的“伪间隙锁”
很多人误以为 RC 彻底移除了间隙锁,其实不然。只要涉及唯一约束(主键/唯一索引)的冲突检测,InnoDB 仍会加 NEXT-KEY LOCK——即行锁 + 后继间隙锁。这在 INSERT ... ON DUPLICATE KEY UPDATE 或 REPLACE INTO 场景中尤为明显:
-
INSERT ... ON DUPLICATE KEY UPDATE:冲突时,在冲突行本身加 X 型NEXT-KEY LOCK -
REPLACE INTO:冲突时,在冲突行的下一条记录加 X 型NEXT-KEY LOCK(例如插入值 40 冲突,则锁区间(40,50]) - 多个事务并发写相同 key,但因加锁位置错位(一个锁
(30,40],另一个锁(40,50]),再叠加插入意向锁等待,极易形成循环等待链
如何确认是不是真“升级”了?盯住三个指标
锁升级不会报错,也不会写明确日志,必须靠交叉验证:
- 查
information_schema.INNODB_TRX:若TRX_ROWS_LOCKED达几万,而业务逻辑只该改 1~2 行,基本可断定锁失控 - 看
SHOW ENGINE INNODB STATUS\G中的heap size:超过 1MB 是锁结构严重膨胀的信号 - 运行
SHOW GLOBAL STATUS LIKE 'table_locks%':若table_locks_waited显著上升(尤其table_locks_immediate下降),说明有非 MyISAM 操作正遭遇表级阻塞效应
真正棘手的从来不是锁多,而是锁乱——比如事务 A 先更新 id=100 再更新 id=1,事务 B 反过来操作,哪怕只锁两行,也能秒级触发死锁检测。RC 下的“升级感”,往往就是这种无序+扫描+冲突三者叠加的结果。











