mysql默认隔离级别为repeatable read,根本原因是兼容statement格式binlog:read committed下事务内多次select可能读取不同版本数据,导致主库执行与从库重放sql时行集合不一致,引发主从数据错位。

MySQL 默认隔离级别是 REPEATABLE READ,根本原因不是为了“更强一致性”,而是为兼容早期 STATEMENT 格式的 binlog,防止主从数据不一致 —— 这个设计是历史包袱驱动的权衡,不是理论最优解。
为什么 READ COMMITTED 在 STATEMENT binlog 下会主从不一致
当 binlog_format = 'STATEMENT' 时,MySQL 只记录 SQL 文本,不记录执行时实际读到哪些行。而 READ COMMITTED 允许事务内多次 SELECT 看到不同版本的数据,这就埋下主从执行逻辑错位的隐患。
- 主库上事务 A 执行
DELETE FROM t WHERE id ,此时还没提交 - 事务 B 插入
INSERT INTO t VALUES (95)并提交 - 事务 A 最后提交 → 主库最终没删掉 95(因为读快照时 B 的插入还没发生)
- 但
STATEMENTbinlog 按执行顺序记了INSERT再记DELETE - 从库重放时先插 95,再删所有
id 的行 → 95 被删掉,和主库不一致
REPEATABLE READ 怎么靠锁规避这个风险
REPEATABLE READ 在 InnoDB 中配合间隙锁(GAP LOCK)和临键锁(NEXT-KEY LOCK),让事务 A 在执行 DELETE WHERE id 时,不仅锁住已有记录,还锁住 <code>(-, 100) 这个范围。
- 事务 B 尝试插入
95会被阻塞,直到事务 A 提交或回滚 - 从而强制主库上两个操作变成串行:要么 B 先插、A 后删(A 删掉 95),要么 A 先删完、B 再插(B 插入成功)
- 无论哪种,binlog 记录的语句顺序和实际效果在主从上保持一致
- 注意:这个保护只在
innodb_locks_unsafe_for_binlog = OFF(默认)时生效
ROW 格式 binlog 出现后,RC 其实也能安全用
MySQL 5.1 起支持 binlog_format = 'ROW',它记录的是每行变更前后的镜像,不再依赖 SQL 执行逻辑。这时隔离级别对主从一致性的影响大幅降低。
- 即使设为
READ COMMITTED,主库执行结果和从库重放结果也完全一致 - 大厂线上普遍用
RC + ROW组合:减少锁竞争、提升并发,同时不牺牲一致性 - 但切记:
ROW日志体积更大,网络传输和磁盘 IO 压力上升,尤其批量更新场景 - 验证方式:
SHOW VARIABLES LIKE 'binlog_format';和SELECT @@transaction_isolation;
别忽略存储引擎和 MySQL 版本的隐含约束
默认隔离级别是 InnoDB 的行为,MyISAM 不支持事务;且该设定从 MySQL 5.5(InnoDB 成为默认引擎)才真正落地。更早版本谈“默认隔离级别”本身就不成立。
-
SELECT @@global.transaction_isolation;查全局默认值,但新建连接实际取值还受客户端连接参数影响 - MySQL 8.0+ 默认
binlog_format = 'ROW',但很多老系统仍维持STATEMENT或MIXED - 如果用了
MIXED,MySQL 会自动在某些语句(如含NOW()、UUID())降级为ROW,行为更难预测 - 跨版本升级时,务必确认
binlog_format和transaction_isolation是否被显式覆盖











