RC在互联网高并发场景更稳定,因其每次SELECT读最新已提交版本,避免长事务阻塞purge、undo膨胀及间隙锁导致的死锁;RR仅适用于报表对账等需事务内结果一致的场景。

互联网高并发场景下,RC(Read Committed)比RR(Repeatable Read)更常用,且多数主流业务系统(如电商订单、支付、IM消息)默认或主动切换到了 RC。
为什么 RC 在互联网场景中实际更“稳”
RR 的可重复读靠的是“事务启动时拍快照”,看似安全,但在长事务 + 高更新频率下反而容易出问题:
-
SELECT看不到其他事务已提交的最新数据,导致业务逻辑误判(比如库存校验用旧值,超卖) - 长事务持续持有
read view,阻塞purge线程,undo log无法及时清理,ibdata1持续膨胀 - 幻读在 RR 下靠间隙锁(Gap Lock)解决,但锁范围大、冲突多,在写密集场景下极易引发死锁或锁等待超时(
Lock wait timeout exceeded)
RC 每次 SELECT 都读最新已提交版本,配合应用层重试和幂等设计,反而更可控、更轻量。
RR 到底适合什么场景
RR 不是“过时”,而是适用面窄——它真正发挥价值的场景非常明确:
- 报表类业务:需要事务内多次查询结果严格一致(如财务对账),且查询不频繁、事务短、无高频更新
- 迁移/兼容老系统:Oracle/PostgreSQL 用户迁入 MySQL 时习惯性选 RR,但往往没意识到代价
- 未开启
binlog_format = ROW的主从环境:RR + STATEMENT 格式能避免部分复制不一致(但该组合本身已不推荐)
注意:innodb_locks_unsafe_for_binlog = ON(5.6 及以前)曾让 RC 表现接近 RR,但该参数已被移除,现在 RC 就是标准行为。
切换到 RC 的实操注意事项
不是改个参数就完事,关键点在应用协同:
- 必须确认业务能容忍“非可重复读”:比如用户余额页刷新看到+100元,再刷又变成+200元,这在 RC 下合法且正常
- 所有
UPDATE ... WHERE必须带足够粒度的条件(最好含唯一键或版本号),避免 RC 下因WHERE条件读到旧值导致误更新 - 避免在 RC 下写“先查后更”的逻辑(
SELECT ...; UPDATE ...),应改用UPDATE ... WHERE id = ? AND status = 'pending'原子判断 - 监控
Innodb_row_lock_waits和slow_query_log中带Waiting for table metadata lock的语句——RC 下锁竞争模式变了,老慢 SQL 可能突然变卡
MySQL 8.0+ 的一个隐藏影响
MySQL 8.0 默认启用 transaction_isolation = 'REPEATABLE-READ',但如果你用的是 binlog_format = ROW + slave_parallel_type = LOGICAL_CLOCK(即并行复制),那么从库回放时的隔离效果其实更接近 RC 语义。这意味着:主库设 RR,但从库应用日志时可能“看不见”某些中间态,导致主从间短暂不一致——这种隐式差异比显式切 RC 更难排查。
真正麻烦的从来不是选哪个级别,而是整个调用链路(应用、连接池、ORM、中间件)是否对所选隔离级别的行为有统一认知和兜底能力。比如 MyBatis 的 @Select 方法若被缓存,哪怕数据库是 RC,应用层也可能“固执地”返回旧结果。


















