尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从主从灾难到架构妥协:MySQL Binlog 与隔离级别的演进之路

从主从灾难到架构妥协:MySQL Binlog 与隔离级别的演进之路 在 MySQL 的面试中我们常听到一句定论“生产环境推荐使用 Row 格式的 Binlog 配合 RC读已提交隔离级别。”但为什么是它们早期的 MySQL 为什么默认使用 RR可重复读这背后其实是一场关于数据一致性与高并发性能的架构博弈。今天我们就从一场“主从复制的灾难”说起彻底理清这段技术演进史。1. 灾难现场Statement 格式与 RC 的致命冲突在 MySQL 早期Binlog 默认使用Statement语句格式。它的逻辑很简单主库执行了什么 SQL就原封不动地把这条 SQL 记录到 Binlog 中然后同步给从库重放。在单线程、低并发的环境下这没有任何问题。但在高并发场景下如果配合RC读已提交隔离级别灾难就会发生。场景还原假设主库上有两个并发事务 T1 和 T2初始数据中id为 1~5 的记录age均为 25。T1 事务开始执行SELECT * FROM user WHERE age 20查到了 5 条数据。T2 事务插队执行UPDATE user SET age 18 WHERE id IN (3, 4, 5)并提交。此时主库物理数据中id 3,4,5 的 age 变成了 18。T1 事务继续执行UPDATE user SET name Bob WHERE age 20。由于是 RC 级别T1 会读到 T2 提交后的最新数据。此时只有 id 1, 2 满足条件。主库结果T1 实际只修改了2 条数据。从库的崩溃Binlog 忠实地记录了 T1 的 SQLUPDATE user SET name Bob WHERE age 20并同步给从库。但从库在执行这条 SQL 时并没有 T2 的并发上下文或者 T2 的 Binlog 还没执行到。从库发现 id 1~5 的 age 依然是 25于是大手一挥修改了5 条数据。结局主库改 2 条从库改 5 条。主从数据发生永久性不一致。本质原因Statement 格式只记录了“动作SQL”却丢失了执行动作时的“战场环境并发数据状态”。2. 历史的妥协为什么 MySQL 默认选择了 RR为了解决上述灾难早期的 MySQL 做出了一个架构上的妥协将默认隔离级别设为 RR可重复读。在 RR 级别下事务开启后第一次查询就会生成 ReadView后续所有查询都复用该快照。这意味着无论 T2 怎么修改并提交T1 在整个事务中看到的数据永远是最初的样子。主库 T1始终看到 5 条 age 20 的数据最终更新 5 条。从库收到 SQL更新 5 条。结果主从一致RR 级别通过牺牲一定的并发灵活性引入了间隙锁 Gap Lock为粗糙的 Statement 格式 Binlog 提供了“确定性”的兜底。这是一种经典的以隔离换一致的策略。3. 治本之策Row 格式 Binlog 的降维打击随着互联网业务对并发要求的提高RR 的间隙锁带来的死锁问题日益凸显。MySQL 5.7 引入了更先进的Row行格式Binlog。Row 格式不再记录 SQL 语句而是直接记录“每一行数据被修改前后的具体值”。同样的场景不同的结局主库执行T1 依然只修改了 id 1, 2 两行数据。Binlog 记录### UPDATE user ### WHERE id1, nameA ### SET nameBob ### UPDATE user ### WHERE id2, nameC ### SET nameBob从库执行从库根本不需要去判断WHERE age 20它直接拿着主库给的“身份证id1, id2”精准修改。结果无论主库并发多高、隔离级别是 RC 还是 RR从库都能实现像素级的数据同步。Row 格式从根源上消灭了“上下文丢失”的问题。4. 终极思考Row RC 还是 Row RR既然 Row 格式解决了主从一致性问题那隔离级别是不是可以随便选了目前业界主要分为两派派系 ARow RC国际主流如 Facebook、Twitter优势RC 级别没有间隙锁并发度极高死锁概率低。配合 Row 格式既保证了主从一致又释放了数据库的最大性能。劣势RC 级别下半一致性读Semi-consistent Read的行为较为复杂且在极端幻读场景下业务逻辑需要更严谨。派系 BRow RR国内大厂主流如阿里、美团优势RR 级别提供了更强的隔离性彻底杜绝了幻读。对于很多从 Oracle 迁移过来的传统业务RR 的行为模式更符合开发者的直觉迁移成本更低。劣势间隙锁在高并发批量插入或更新时容易引发死锁对开发人员的 SQL 编写规范要求更高。总结从 Statement 到 Row从 RR 到 RCMySQL 的演进史就是一部不断解耦“执行逻辑”与“同步逻辑”的历史。Statement RC因丢失上下文导致主从灾难。Statement RR通过强隔离换取一致性是历史的无奈之举。Row RC/RR通过记录物理数据变更彻底解放了隔离级别的束缚。
返回列表