读已提交(RC)通过语句级快照读、仅对匹配行加行锁且执行完立即释放、不加间隙锁,显著减少锁等待和MVCC版本链长度,提升高并发吞吐量;但要求业务能容忍不可重复读。

因为读已提交(Read Committed)在避免脏读的前提下,显著降低了锁持有时间与 MVCC 版本链长度,从而提升并发吞吐量——这是高并发业务最直接可感知的收益。
读已提交如何减少行锁等待
在 READ COMMITTED 下,InnoDB 对普通 SELECT 不加锁(快照读),且对 UPDATE/DELETE 操作只在匹配行上加行锁,**语句执行完立即释放锁**。而 REPEATABLE READ 会为整个扫描范围加间隙锁(gap lock)或临键锁(next-key lock),锁持续到事务结束。
- 例如:执行
UPDATE orders SET status = 'shipped' WHERE user_id = 123 AND status = 'paid',在 RC 下,只锁住实际命中的几行;在 RR 下,可能锁住user_id = 123对应的整个索引区间,阻塞其他用户插入新订单 - 高并发写入场景(如秒杀、订单创建)中,锁范围越小、持有越短,事务回滚/重试概率越低,TPS 越稳定
- 注意:若查询无有效索引,RC 仍可能触发全表扫描+行锁,但不会升级为范围锁
MVCC 快照生成策略差异直接影响读性能
READ COMMITTED 每条 SELECT 语句都生成独立的 Read View,只看到该语句执行时刻已提交的数据;REPEATABLE READ 在事务第一次读时生成一个全局 Read View 并复用。
- RC 的 MVCC 版本链更短:因为旧版本只需保留到“最近一次语句开始前”仍活跃的事务结束即可;RR 需保留到“事务第一次读开始前”所有活跃事务结束,Undo Log 清理延迟更高,容易引发
Undo log too large或长事务阻塞 purge 线程 - 在读多写少但写入频繁的场景(如用户中心资料页 + 实时积分更新),RC 能更快回收历史版本,降低 buffer pool 压力
- 副作用:同一事务内两次
SELECT可能返回不同结果(不可重复读),但多数互联网业务(如展示用户当前余额、最新订单状态)恰恰需要这个“实时性”
为什么不是所有高并发场景都适合读已提交
RC 的“实时性”在某些逻辑闭环里会破坏业务一致性,此时必须退回到 REPEATABLE READ 或加应用层控制。
- 典型不适用场景:转账操作中需先查余额再扣款——若两次读之间被其他事务修改并提交,RC 下可能发生“超扣”(第一次读 100 元,中间转入 50 元,第二次读 150 元,但扣款逻辑仍按原始 100 元判断,导致余额变负)
- 幻读虽在 RC 和 RR 中都存在,但 RR 下可通过
SELECT ... FOR UPDATE加临键锁规避插入干扰;RC 下即使加锁,也无法阻止其他事务在间隙中插入新行后提交 - 如果你依赖存储过程或触发器做多步校验,且这些步骤跨多条语句,RC 的每次语句级快照会让逻辑难以预测
真正关键的不是“高并发”本身,而是业务是否容忍单事务内读取结果随外部提交而变化。很多团队盲目切 RC 是为了对标 PostgreSQL 或缓解主库锁争用,却忽略了自身 SQL 是否隐含“读-判-写”强依赖——这种细节,往往在线上慢日志或死锁日志里才暴露出来。


















