新闻详情

新闻详情

首页 / 资讯中心 / 详情

MySQL两阶段提交:redo log与binlog如何保证崩溃恢复一致性

发布时间:2026/10/1 11:48:23来源:尧图网络
MySQL两阶段提交:redo log与binlog如何保证崩溃恢复一致性
面试里MySQL高频题排个序redo log 和 binlog 为什么要做两阶段提交绝对能进前五。很多人能背出结论先写 redo log再写 binlog中间借一个 prepare 状态兜底。但要追问一句崩溃恢复时MySQL 到底凭什么判定一个事务是提交还是回滚能回答利索的人立刻少一大半。这个问题难就难在它把两套平时各干各的日志系统硬拉到一起。redo log 归 InnoDB 管binlog 归 Server 层管前者负责崩溃恢复后者负责主从复制和时间点恢复。可事务提交的那一刻这两份日志必须保持某种一致性——要么都有要么都没有。MySQL 的解法是借用了 XA 分布式事务里两阶段提交的经典思路在组件内部做了一次收税式的协调。这篇文章把这条线完整捋一遍适合正在准备 MySQL 面试、或工作中被主从数据不一致问题折磨的同行。1. 日志为什么分裂成两套体系先把最基础的背景对齐redo log 和 binlog 不是同一层的东西它们的出生时间、记录格式、归属方都不一样。不理解这个前提后面两阶段提交的很多设计意图就看不出来。1.1 Server 层与 InnoDB先有 binlog后有 redo logMySQL 的架构是分层的Server 层负责连接管理、SQL 解析、优化和执行存储引擎层负责数据到底怎么落盘。binlog 归 Server 层管所以任何存储引擎都能用redo log 是 InnoDB 引擎自带的MyISAM 里就没有这东西。历史原因也很直接最初的 MySQL 靠 binlog 做复制和恢复后来 InnoDB 成为默认引擎带来了一套完整的崩溃恢复机制核心就是 redo log 加 undo log。binlog 已经承担了主从复制和备份恢复的职责不可能为了统一就砍掉于是两套日志体系就共存了下来。这个历史包袱恰恰决定了今天的问题一个事务在 InnoDB 里已经改了一堆数据页在 Server 层也产生了一条完整的逻辑变更记录两份记录都必须永久保存。如果只保证一份另一个在崩溃恢复时就会露馅。1.2 物理日志和逻辑日志完全不同的记录方式很多人把 redo log 和 binlog 都笼统叫日志但它们的记录粒度根本不在一个维度对比项redo logbinlog所属层级InnoDB 存储引擎层MySQL Server 层记录内容物理页修改页号、偏移量、修改后数据逻辑变更SQL 或行前后像写入方式循环覆盖写入历史会被抹掉追加归档可以一直保留核心用途崩溃恢复重放物理修改主从复制、基于时间点恢复、审计格式InnoDB 私有Server 层通用可被下游工具解析打个比方redo log 像油漆工在墙上刷漆时留下的精确坐标记录——第 3 面墙距地面 120 厘米宽度 40 厘米刷成了米黄色binlog 则像施工日志——今天把客厅东墙刷成了米黄色。前者是物理位置的精确描述后者是业务语义的描述。即使 binlog 用 row 格式记录了行变前后的值它依然属于逻辑层面的记录因为它不关心数据在磁盘页的哪个偏移位置。1.3 只留一套行不行这个问题问得很值钱因为它能帮你理解为什么必须两套配合。只留 binlog 行不行不行。崩溃恢复时 InnoDB 需要把数据页恢复到修改后的精确状态binlog 做不到按页、按偏移量重放事务执行过程中产生的中间状态比如大事务改了 10 万行期间每个页的中间版本binlog 也不记录。更重要的是binlog 在事务提交阶段才会写入InnoDB 崩溃恢复必须在提交判定之前就有能力重建数据页宾主顺序就对不上。只留 redo log 行不行也不行。redo log 是循环写的旧记录会被覆盖你没法拿它做基于时间点的恢复而且它是 InnoDB 私有格式复制到从库时要跨引擎解析基本不可能。主从复制、历史溯源这些 Server 层的需求必须靠 binlog。所以两套日志必须共存。共存就带来一个尖锐问题提交事务时这两份日志的写入顺序怎么安排才算安全这就引出两阶段提交。2. 提交瞬间的临界区先写谁都会出事事务提交的过程本质上是在处理一个跨组件的原子性问题。InnoDB 的数据修改要交给 redo log 保障持久性从库要依靠 binlog 做回放两边必须看到同一个事务。如果你不做协调就是一场灾难。2.1 先写 redo 或先写 binlog 的失序场景假设没有两阶段提交MySQL 只是简单固定一个写入顺序。先写 redo log再写 binlog如果 redo log 落盘成功、binlog 还没写的时候崩溃崩溃恢复时 InnoDB 会因为 redo log 里有完整记录而把事务提交主库数据生效了但 binlog 里根本没有这个事务从库和后续的备份恢复都少了它。主库比从库多一笔数据主从直接分叉。先写 binlog再写 redo log如果 binlog 落盘成功、redo log 还没写的时候崩溃情况反过来。从库会把 binlog 里的这个事务执行掉但主库 InnoDB 崩溃恢复时没有这个事务的 redo 记录主库数据没有它。从库比主库多一笔数据同样是分叉。两条路都走不通。你以为换个顺序能解决其实无论怎么调崩溃点只要落在两份日志之间就必然出现一边有、一边没有的窗口。这个问题的本质是日志分属两个组件没有一个协调者告诉两边事务到底通没通过。两阶段提交就是来补这个缺的。2.2 prepare 阶段redo log 先落地的预备役两阶段提交的第一阶段叫 prepare。事务在执行过程中InnoDB 会持续生成 redo log记录它对数据页的每次物理修改。提交时第一步就是把这些 redo log 刷到磁盘并把本事务在 redo log 中的状态标记为 prepare。这里要搞明白prepare 不是提交的意思它只是说InnoDB 这边已经准备好修改记录不会丢了但我还不能最终拍板。为什么不能直接标记 commit因为 Server 层的 binlog 还没写完此刻事务的最终命运还没定。万一 binlog 写入失败你还得把 InnoDB 这边的修改回滚掉不能提前对外宣称成功。与此同时MySQL 会给这个事务分配一个内部 XID。InnoDB 刷盘的 redo log 里会带上这个 XID为后面的接头做准备。2.3 commit 阶段binlog 落盘才是真正的提交点两阶段提交的第二阶段才轮到 binlog 登场。Server 层把这事务产生的 binlog 事件写入 binlog 文件落盘成功后在末尾写一个 XID_EVENT里面记录同一个 XID。这里藏着 MySQL 最核心的一条约定binlog 落盘成功的那一刻事务才算真正提交。为什么把提交点放在 binlog 这边而不是 InnoDB 那边因为 binlog 是复制的地基。如果 binlog 没写成功说明事务没有真正对外生效从库不会执行它一旦 binlog 写成功即使 InnoDB 还没来得及标记 commit主库也必须认下这个事务。否则从库执行了、主库没有分叉马上出现。binlog 落盘之后InnoDB 才把 redo log 里的事务状态由 prepare 改成 commit事务正式完成向客户端返回成功。把一条 UPDATE 的完整日志旅程列出来感受会更直观UPDATE user SET balance balance 100 WHERE id 1;InnoDB 把 id 1 所在的数据页读入 buffer pool如果不在内存。写 undo log用于将来回滚。修改内存中的页同时生成对应的 redo log记录这个页哪个偏移改了、改成了什么。提交事务先把本事务的 redo log 刷盘状态标记为 prepareLSN 推进。Server 层把 UPDATE 产生的 binlog 事件写入 binlog 文件并落盘末尾带 XID_EVENT。InnoDB 把 redo log 里的事务状态更新为 commit。第 4、5、6 步就是两阶段提交的完整路径。这里讲的逻辑模型8.0 在实现上会和组提交流程融合得更深但核心语义不变。3. 崩溃恢复的裁判规则谁说了算两阶段提交不只是提交时的流程设计它真正的价值体现在崩溃恢复那几毫秒里。恢复过程不是拿到 redo log 就从头到尾无脑重放而是先做一轮排位赛。3.1 恢复前的状态判定崩溃恢复开始后InnoDB 会从最近的 checkpoint 位置开始扫描 redo log把所有事务按状态分类。关键问题是redo log 里的 prepare 状态事务到底算不算数这时必须让 binlog 出场当证人。判定规则可以这样理解redo log 中的状态binlog 中是否有对应 XID事务最终判定prepare无回滚prepare有且事务事件完整提交并重放commit有必然存在提交并重放3.2 prepare 加无 binlog回滚事务在 prepare 阶段崩溃binlog 还没来得及写说明它没跨过提交点。主库不能提交这个事务InnoDB 会借助 undo log 把它回滚。从库呢binlog 里根本没有这个事务自然也不会执行。两边都当它没存在过一致。3.3 prepare 加 binlog 完整提交这是最反直觉的一条redo log 明明还停在 prepare 状态凭什么判定成提交因为 binlog 写成功已经意味着事务跨过了提交点。如果主库在这里选择回滚从库却已经从 binlog 里把事务执行掉了从库就会比主库多一笔数据。为了防止这个分叉主库必须提交并把 redo log 状态补齐成 commit然后重放修改。3.4 commit 状态直接提交redo log 里事务已经标记为 commit说明两阶段提交流程走完了binlog 必然已经落盘。这种情况直接重放修改没有争议。3.5 XID两个日志之间的接头暗号崩溃恢复中InnoDB 怎么知道 binlog 里哪个事务对应 redo log 里的哪个事务靠 XID。每个事务的 binlog 末尾都有 XID_EVENT记录了这个事务的 XID。用 mysqlbinlog 能看到类似这样的输出mysqlbinlog --base64-outputdecode-rows -v mysql-bin.000001 | tail -n 20输出末尾长这样# at 420 #230511 10:27:33 server id 1 end_log_pos 449 CRC32 0x... Xid 112233 COMMIT/*!*/;后面的 COMMIT 也有自己的格式通常在 Xid 事件之后紧跟着出现。这里的Xid 112233就是事务在 binlog 侧落下的身份标记。恢复时InnoDB 会把 redo log 里断点事务的 XID 列表拉出来和 binlog 里能找到的 XID 列表做一次比对。用伪代码示意这个判定流程注意这不是 MySQL 源码只表达思路崩溃恢复开始: 从 checkpoint_lsn 开始扫描 redo log 对每个事务 T: 若 T 状态 commit: 把 T 加入 commit_set 若 T 状态 prepare: 若 binlog 中存在 T.xid 且事务事件完整: 把 T 加入 commit_set 否则: 把 T 加入 rollback_set 重放阶段: 从 checkpoint_lsn 开始重放 redo log 只重放 commit_set 中事务的修改 回滚阶段: 对 rollback_set 中的事务用 undo log 回滚逻辑不复杂但它是整个两阶段提交的地基。4. 性能与安全的权衡组提交和两个关键参数两阶段提交说再多落到实际生产环境躲不开性能和安全的拉扯。每笔提交都让 redo log 和 binlog 各自刷一次磁盘在高并发下是很大的开销。所以 MySQL 做了一系列优化其中最重要的就是组提交。4.1 组提交把 N 次 fsync 合并成 1 次fsync 的代价很高一次磁盘同步意味着把数据真正落到存储介质上而不是停留在操作系统缓存。如果每笔事务都做两次 fsyncredo 一次、binlog 一次TPS 会被磁盘拖死。组提交的思路是高并发场景下多个事务会几乎同时到达提交点。与其一笔一笔地写不如攒一批一起落盘。实现上大致分三个阶段flush 阶段把多个事务的 binlog 从各自的 binlog cache 写入 binlog 文件write还没刷盘。sync 阶段对 binlog 文件做一次 fsync把这批事务的 binlog 一起落到磁盘。commit 阶段按事务原来的提交顺序逐个在 InnoDB 中把事务标记为 commit。这样本来需要 N 次 fsync 的操作合并成一次吞吐量一下子拉开差距。MySQL 5.6 引入了 binlog 组提交5.7 又把 redo log 的刷盘和这个流程做了融合效果更明显。实操建议如果你的写入并发很高但磁盘 fsync 能力有限不要急着加硬件先确认组提交相关参数没有被显式关闭。正常情况下它是默认开启的你会在高并发小事务场景明显感觉到吞吐比低频 fsync 的场景平滑很多。4.2 两个刷盘参数四类组合的风险差异除了组提交还有两个参数是 DBA 必须刻在脑子里的innodb_flush_log_at_trx_commit 和 sync_binlog。innodb_flush_log_at_trx_commit1每次事务提交都让 redo log 刷盘最安全。2每次提交只把 redo log 写到操作系统缓存每秒刷一次盘。0交给系统调度每秒刷盘提交时不主动处理。sync_binlog1每次事务提交都让 binlog 刷盘最安全。0交给操作系统决定何时落盘。N每 N 次提交刷一次盘。两组参数一组合安全性差异就显出来了innodb_flush_log_at_trx_commitsync_binlog崩溃可能的结果典型场景11不丢已提交事务两阶段提交完整金融、支付等高一致性业务10redo 稳binlog 可能缺最近已提交事务从库有延迟/缺失风险数据安全要求较高但接受复制延迟01binlog 稳redo 每秒刷进程崩溃可能丢 1 秒内已提交事务高并发写入、能容忍少量丢失21同上binlog 稳redo 可能缺最近 1 秒高并发写入、能容忍少量丢失0 或 20redo、binlog 都不即时落盘风险叠加不建议生产使用这里要提醒一个容易踩的误区只要不是双 1两阶段提交的严格保证就已经被削弱了。最典型的场景是 innodb_flush_log_at_trx_commit 2、sync_binlog 1binlog 每次提交都落盘但 redo log 每秒才刷一次。如果进程恰好崩溃在 binlog 落盘之后、redo 刷盘之前恢复后 binlog 里有这个事务redo log 里却没有对应的 prepare 记录。从库会执行该事务主库 InnoDB 不认这笔账主从就会分叉。所以我的建议很直白金融、电商核心交易这类不能接受主从分叉的业务老老实实双 1。如果确实对性能压测结果不满意优先考虑优化磁盘比如用 SSD、分离日志盘而不是调这两个参数。组提交已经帮你在软件层做了大量优化参数层面省下来的那点 fsync 往往不值得拿一致性去赌。4.3 主从架构下的额外兜底如果你业务选型确实用不了双 1又想降低分叉概率可以搭配半同步复制。半同步复制的核心是主库提交事务后至少等一个从库确认收到并落盘了 binlog才向客户端返回成功。这实际上把两阶段提交的提交点扩展到了主从链路虽然不能完全替代双 1但能把丢失窗口压得极窄。实际生产里我见过不少团队用 redo 2 binlog 1 半同步复制的组合换取比双 1 更高的吞吐同时把风险控制在一定范围内。要不要抄这套方案取决于你的业务对极端情况下主从可能分叉的容忍度。如果一点偏差都不能接受请回到双 1。5. 现场排查把两阶段提交看成具体数字两阶段提交听起来抽象但排查问题时它最后会落成一个一个数字、一条一条日志。这里分享几个我常用的现场排查动作。5.1 用 SHOW ENGINE INNODB STATUS 观察 redo log在数据库里执行SHOW ENGINE INNODB STATUS\G重点看 TRANSACTIONS 部分上方的 LOG 字段--- LOG --- Log sequence number 8450276537 Log flushed up to 8450270100 Pages flushed up to 8449912200 Last checkpoint at 8449801000四个指标的含义Log sequence number当前 redo log 已经写到的位置理解为日志里程表当前读数。Log flushed up toredo log 已刷到磁盘的位置。Pages flushed up to已经刷到磁盘的数据页中最高记录到的 LSN。Last checkpoint at最近一次 checkpoint 的位置这个水位线之前的 redo 日志可以循环覆盖。排查技巧如果 Log sequence number 和 Log flushed up to 差距一直很大说明 redo 刷盘跟不上有积压。Last checkpoint at 落后太多则说明脏页刷盘慢checkpoint 推进不积极此时 redo log 循环写可能很快到达容量上限后续会出现刷盘动作加剧。注意MySQL 5.7 的 redo log 文件是 ib_logfile0、ib_logfile18.0.30 之后改成了 #innodb_redo 目录下的多个文件循环写的原理不变。5.2 用 mysqlbinlog 核实事务边界与 XID排查事务到底提交没有直接看 binlog 尾部mysqlbinlog --base64-outputdecode-rows -v mysql-bin.000001 | grep -i Xid能看到一串事务的 Xid每个 Xid 对应一个已提交事务。如果某个事务在主库 binlog 里找不到对应的 Xid而 redo log 里却有 prepare 状态那它大概率是在两阶段提交的 prepare 阶段就崩溃了。还可以精确查看某个位置的 binlog 内容mysqlbinlog --start-position400 --stop-position500 mysql-bin.000001输出里的事务末尾会带上 Xid 数字 和 COMMIT 关键字这就是两阶段提交在 binlog 侧留下的最终痕迹。5.3 排查主从数据不一致时的切入顺序遇到从库比主库多数据/少数据不要一上来就翻业务代码先对比两份日志的一致性。第一步看主库当前 binlog 位置SHOW MASTER STATUS;第二步看从库的复制进度SHOW SLAVE STATUS\G重点关注 Relay_Master_Log_File 和 Exec_Master_Log_Pos这两个值和主库的 File、Position 对照能看出从库落后多少。第三步如果从库执行的 binlog 位置和主库当前写入位置差得很远排查位置附近的事务是否完整。把从库 relay log 中出问题那个事务的 Xid 拿出来去主库 binlog 里确认这个事务是否真的存在、是否完整。两阶段提交保证的是binlog 里有主库 InnoDB 就必须认所以一旦发现主库 binlog 有、从库没执行或执行到一半问题大概率不在两阶段提交而在复制链路本身的故障。最后留一个排查习惯给大家遇到这个事务到底提交了没有的争论别凭感觉直接把两份日志拿出来对 XID。redo log 里有 prepare 标记、binlog 里有对应 Xid事务就算数缺一边就按未提交处理。两阶段提交听起来是个很高大上的协议落实到排查动作上其实就是找一个接头暗号。把这条逻辑想透了MySQL 的崩溃恢复和主从一致性对你来说就不再是黑盒。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

中文预训练模型选型与加载实战指南 2026/10/1 17:24:25

中文预训练模型选型与加载实战指南

简介:本资源是一套面向人工智能开发者与NLP研究者的高质量中文预训练模型集合,聚焦预训练模型选型、部署与下游任务适配等实际工程问题,特别适合需兼顾效果与推理效率的工业级文本理解场景。压缩包共211个文件,以123个Python脚本&…

阅读更多 →
微信防撤回一步到位:RevokeMsgPatcher 新手指南(Windows 微信/QQ/TIM) 2026/10/1 17:24:25

微信防撤回一步到位:RevokeMsgPatcher 新手指南(Windows 微信/QQ/TIM)

微信防撤回一步到位:RevokeMsgPatcher 新手指南(Windows 微信/QQ/TIM) 【免费下载链接】RevokeMsgPatcher :trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁(我已经看到了,撤回也没用了&#…

阅读更多 →
Python+pyQt语义分割GUI:8个预训练模型统一调用与批量对比 2026/10/1 17:24:25

Python+pyQt语义分割GUI:8个预训练模型统一调用与批量对比

简介:这是一套基于Python与PyQt构建的图像语义分割桌面软件项目,面向计算机、人工智能、通信工程等专业的在校学生与开发者,可用于毕业设计、课程设计、作业提交或项目初期立项演示。项目集成mobilenet、resnet50等8种主流分割模型&#xff0…

阅读更多 →
110张熊猫图双格式数据集:VOC与YOLO标注解析及YOLOv8训练实战 2026/10/1 17:24:24

110张熊猫图双格式数据集:VOC与YOLO标注解析及YOLOv8训练实战

简介:这是一份面向目标检测初学者与算法验证人员的熊猫单类别数据集,采用Pascal VOC与YOLO双格式标注,可直接用于YOLO、Faster R-CNN等主流框架的训练与测试,省去格式转换的繁琐步骤。压缩包共332个文件,包含110张jpg原…

阅读更多 →
Python+SVM舆情分析系统实战:从Scrapy爬虫到情感分类全链路解析 2026/10/1 17:24:24

Python+SVM舆情分析系统实战:从Scrapy爬虫到情感分类全链路解析

简介:这套项目是一个基于Python与支持向量机的微博舆情分析系统,完整覆盖数据采集、情感分类与Web可视化三大环节,面向毕业设计、课程设计或工程实训人群,也适合希望掌握爬虫、机器学习与Web开发整合流程的进阶学习者。系统按模块…

阅读更多 →
SNTP服务器程序从部署到落地:协议原理、客户端对接与避坑指南 2026/10/1 17:24:17

SNTP服务器程序从部署到落地:协议原理、客户端对接与避坑指南

简介:这是一份面向网络编程初学者与嵌入式开发者的 SNTP 服务器程序源码包,用于在局域网内搭建轻量级时间同步服务,解决设备时钟不一致的问题。压缩包共 6 个文件,以 3 个 C 源文件与 2 个头文件为主体,分别承担时间同…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉