半一致性读是InnoDB在READ COMMITTED隔离级别下对UPDATE语句的优化行为:当扫描到被锁行时,若其最新提交版本不满足WHERE条件,则跳过该行、不加锁不等待;仅当版本匹配才加锁。它非独立机制,不改变语义,也不适用于REPEATABLE READ。

MySQL 没有“半一致性读”这个官方机制,UPDATE 语句中实际起作用的是**半一致读(semi-consistent read)**,它是 InnoDB 在特定条件下为减少锁冲突而启用的优化行为,仅在 READ COMMITTED 隔离级别下生效。
半一致读只在 READ COMMITTED 下触发
它不是独立的隔离级别,也不是可开关的特性,而是 InnoDB 在 READ COMMITTED 下对 UPDATE ... WHERE 执行路径的一个内部优化。在 REPEATABLE READ 下,InnoDB 始终使用当前读(current read),会加临键锁(next-key lock),不走半一致读逻辑。
-
READ COMMITTED:InnoDB 先用非锁定读(consistent read)定位符合条件的记录;若发现某行已被其他事务加锁,且该行最新版本不满足WHERE条件,则不等锁释放,直接跳过——这就是半一致读的核心动作 -
REPEATABLE READ:无论是否被锁、是否满足条件,只要扫描到该行,就尝试加锁(包括 gap 锁),可能触发锁等待甚至死锁
UPDATE 中半一致读如何减少锁等待
典型场景是并发 UPDATE t SET x=1 WHERE id=10。假设事务 A 已对 id=10 加了行锁但未提交:
- 事务 B 在
REPEATABLE READ下执行相同语句 → 立即阻塞,等待事务 A 释放锁 - 事务 B 在
READ COMMITTED下执行 → 先做 consistent read 查出id=10的最新可见版本;若该版本的id ≠ 10(比如被删或更新后不匹配 WHERE),则跳过该行,不加锁也不等待;只有当版本满足 WHERE 条件时,才去获取行锁
这能显著降低因“查到但不改”导致的无谓锁等待,尤其在高并发、WHERE 条件较宽泛(如 WHERE status = 'pending')时效果明显。
它不改变语义,也不绕过 MVCC 可见性判断
半一致读不是跳过 MVCC 判断,恰恰相反:它依赖 consistent read 获取的版本来做是否跳过的决策。关键点包括:
- 跳过行为只发生在「锁存在 + 当前版本不满足 WHERE」时;如果版本满足 WHERE,仍会正常加锁、等待或报错
- 跳过的是单行,不影响其他行的扫描和加锁逻辑
- 不会导致“幻读”被忽略——gap 锁依然按需加,只是不因“已锁但不匹配”而卡住
- 日志里看不到显式开关,无法通过
SHOW VARIABLES或INFORMATION_SCHEMA查看是否启用,它由隔离级别隐式控制
容易被误认为“半同步”或“读未提交”
名字里的“半”常引发误解,但它和复制层的 rpl_semi_sync_* 完全无关,也和 READ UNCOMMITTED 无关。它只是 InnoDB 在 RC 级别下对 UPDATE 路径的一次“先看再锁”的短路优化。真正要注意的其实是:如果你依赖锁等待来实现某种串行逻辑(比如防重下单),切到 READ COMMITTED 后,部分 UPDATE 可能悄无声息地跳过,而不是阻塞——这种静默行为比超时更难排查。


















