mysql默认隔离级别是rr,核心原因是早期binlog仅支持statement格式时,rc会导致主从执行顺序错位引发数据不一致;rr通过间隙锁强制串行化写入,保障复制安全。

MySQL 默认用 RR(Repeatable Read)隔离级别,核心原因不是为了“更强的一致性”,而是为了解决早期主从复制中 STATEMENT 格式 binlog 下的**数据不一致风险**——尤其在并发写入场景下,RC 会导致主库执行顺序和从库回放顺序错位。
为什么 RC + STATEMENT binlog 会主从不一致
在 MySQL 5.1 之前,binlog 只支持 STATEMENT 格式,它记录的是原始 SQL 文本,而非实际变更的行。当多个事务并发修改同一范围数据时,RC 下的读写行为不锁定间隙,导致执行顺序不可控:
- Session A 执行
DELETE FROM t WHERE c_id = 2(删掉 2 条) - Session B 紧接着执行
INSERT INTO t VALUES (100, 2)(插入 1 条) - 主库上因锁等待或调度,实际执行是先删后插;但
binlog按提交顺序记为 “先插后删” - 从库回放时严格按
binlog顺序执行 → 先插再删 → 最终比主库少 1 行
这种错位在 RC 下无法避免,因为 RC 不加间隙锁(gap lock),不阻塞其他事务在相同索引区间插入。
RR 如何靠间隙锁堵住这个漏洞
RR 隔离级别下,InnoDB 对 WHERE 条件涉及的索引范围自动加 Next-Key Lock(行锁 + 间隙锁)。上面例子中:
- Session A 的
DELETE ... WHERE c_id = 2会锁住c_id = 2对应的索引间隙 - Session B 的
INSERT ... (100, 2)尝试往该间隙插入 → 被阻塞,直到 Session A 提交或超时 - 结果:两个操作变成串行化执行,
binlog记录顺序与主库物理执行顺序严格一致
这并非为了业务逻辑上的“可重复读”,而是为复制安全强制引入的副作用级保护机制。
为什么现在仍默认 RR,即使已普遍用 ROW 格式
ROW 格式 binlog 记录的是具体行变更,天然规避了语句重放顺序问题,理论上 RC 也能保证主从一致。但默认值未改,是因为:
- 兼容性:大量存量应用、ORM、中间件(如 ShardingSphere)依赖
RR的快照行为和锁语义 - MVCC 行为差异:
RR下事务启动时创建一致性视图,整个事务内看到相同快照;RC每次SELECT都新建视图,可能读到刚提交的新版本 —— 这对某些业务逻辑(如两次查询校验)是隐式契约 - 幻读虽未完全消除,但
RR的Next-Key Lock至少在当前读(SELECT ... FOR UPDATE)中能拦截大部分插入冲突,而RC连这个防护都没有
真正容易被忽略的点是:RR 的“安全性”只在配合 STATEMENT binlog 时是刚需;一旦切到 ROW,它的优势就大幅缩水,反而因更长的锁持有时间和更大的 MVCC 版本链带来额外开销。要不要切 RC,得看你的 binlog 格式、是否重度依赖间隙锁语义、以及能否接受每次查询都看到最新已提交数据 —— 而不是想当然认为“默认就是最稳妥”。











