MySQL默认隔离级别是REPEATABLE READ,根本原因是早期STATEMENT格式binlog下RC会导致主从执行逻辑错位;RR通过Next-Key Lock强制写操作串行化,确保binlog记录与物理执行一致。

MySQL 默认用 REPEATABLE READ,不是因为“它更安全”或“更适合业务”,而是为了在早期主从复制机制下堵住一个致命漏洞:STATEMENT 格式 binlog + READ COMMITTED 会导致主从数据不一致。
STATEMENT binlog 下 RC 必然引发主从错位
MySQL 5.1 之前只支持 STATEMENT 格式 binlog,它只记录 SQL 文本,不记录实际影响的行。当两个事务并发操作同一索引范围时:
- Session A 执行
DELETE FROM t WHERE c_id = 2(未提交) - Session B 紧接着执行
INSERT INTO t VALUES (100, 2)并提交 - 主库物理执行顺序可能是“先删后插”,但 binlog 按提交时间记为“先插后删”
- 从库回放时严格按 binlog 顺序执行 → 先插再删 → 最终比主库少一行
这个错位在 READ COMMITTED 下无法避免,因为它不加间隙锁,不阻塞 Session B 的插入。
RR 靠 Next-Key Lock 强制串行化写入
REPEATABLE READ 下,InnoDB 对 WHERE 条件涉及的索引范围自动加 Next-Key Lock(行锁 + 间隙锁):
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- Session A 的
DELETE WHERE c_id = 2会锁住c_id = 2对应的整个索引区间 - Session B 的
INSERT (100, 2)尝试往该区间插入 → 被阻塞,直到 Session A 提交或超时 - 结果:两个操作在主库上变成确定性顺序,binlog 记录与物理执行完全对齐
这不是为业务提供“可重复读”语义,而是为复制安全强加的锁机制。
为什么现在还默认 RR,即使已普遍用 ROW binlog
ROW 格式 binlog 记录的是变更前后的行镜像,天然规避语句重放歧义,READ COMMITTED + ROW 完全可保证主从一致。但默认值没改,因为:
- 大量 ORM(如 MyBatis + Spring Transaction)隐式依赖事务内多次查询结果一致 ——
REPEATABLE READ复用 ReadView,READ COMMITTED每次 SELECT 都新建 - 分库中间件(如 ShardingSphere)解析 SQL 时,会根据隔离级别推断锁行为;RR 的间隙锁语义被部分路由策略当作前提
- DBA 运维脚本、备份工具(如
mydumper)、审计插件常假设“RR 下写操作会被间隙锁阻塞”,换 RC 后可能漏掉并发冲突预警 -
Innodb_row_lock_time_avg等监控指标基线值是基于 RR 下锁竞争强度建立的;切 RC 后数值骤降,反而触发误告警
真正容易被忽略的点
REPEATABLE READ 的“安全性”只在配合 STATEMENT binlog 时是刚需;一旦切到 ROW,它的优势就大幅缩水,反而因更长的锁持有时间、更大的 MVCC 版本链、更慢的 purge 线程带来额外开销。而幻读在 RR 下也并未真正解决——只要 WHERE 条件不走索引(比如 WHERE b = 1,而 b 列无索引),InnoDB 就不会加间隙锁,新插入的数据仍能被后续查询看到。

















