rc下半一致性读仅在update/delete扫描到被x锁行且执行计划为全表或二级索引扫描时触发,主键精确查找不启用;它非性能优化而是降低锁冲突的兜底机制。

RC隔离级别下,半一致性读只在UPDATE/DELETE语句扫描到已被其他事务加X锁的行时才触发,且仅对全表扫描或二级索引扫描路径生效;主键/聚簇索引精确查找不会走这套逻辑。
半一致性读触发的两个硬性条件
它不是默认开启的“读优化”,而是被阻塞时的兜底策略,必须同时满足:
-
transaction_isolation必须为READ-COMMITTED(MySQL 8.0+ 已移除innodb_locks_unsafe_for_binlog参数,不再支持通过该参数开启) - 执行计划必须是全表扫描(
type: ALL)或二级索引扫描(type: index/range),且WHERE条件未命中覆盖索引
如果语句走了主键或唯一索引等精准定位路径,InnoDB 直接尝试加锁或跳过,根本不会构造历史版本——半一致性读压根不介入。
UPDATE执行时半一致性读的实际流程
当UPDATE碰到一个已被其他事务加了X锁的行,InnoDB不立即等待,而是:
- 返回该行的最新已提交版本(从undo log构造)给MySQL Server层
- MySQL Server用这个旧版本重新判断
WHERE条件是否匹配 - 若匹配(即这行确实该更新),MySQL再发起第二次读请求,这次InnoDB会加锁或等待(进入常规锁流程)
- 若不匹配,直接跳过该行,不加锁、不等待
注意:这个“两次读”不是用户可控的,是引擎与Server层之间隐式协作。你看到的慢,往往是因为第一次读返回旧版本后,Server层判定匹配,又触发第二次锁竞争——尤其在高并发下,LOCK_SYS mutex 成为热点,CPU耗在 ut_delay 上。
为什么没索引会让半一致性读显得更慢
表面看是“半一致性读变慢”,实际是缺失索引暴露了底层开销:
- 全表扫描导致大量无关行被逐个检查,每碰到一个被锁行,都得走一遍版本构造 + Server层判断 + 可能重读
- 上下文在InnoDB和Server层之间频繁切换,CPU和内存开销上升
- 即使最终没更新任何行,I/O和CPU资源已在扫描过程中被大量消耗
典型例子:UPDATE t SET d=20 WHERE c=20,而 c 列无索引——在RC下看似避免了全表锁,但性能反而比RR更差,因为RR至少可能利用索引下推或提前终止,而RC的半一致性读在无索引场景下成了“高开销兜底”。
容易被忽略的关键点
半一致性读不是性能优化特性,而是RC隔离级别下降低锁冲突概率的副作用机制。它解决的是“不该锁的别锁”,而不是“怎么更快地锁”。真正影响UPDATE性能的,永远是索引设计是否让扫描路径足够窄——只要能走主键或高效二级索引,半一致性读根本不会启动;一旦被迫全表扫,它只是让锁等待变少,但执行本身更重了。











