Yihui’s Blog

MySQL主库故障如何处理?故障切换的方案有哪些?

日期:2026-09-27
标签:#面试 #MySQL #数据库
难度:中等
来源:牛面 MySQL 题库
答案说明:独立整理(站内题目标记为 VIP,未读取会员答案)

一句话答案

先判定故障并阻断旧主写入,再核对候选副本事务完整性、提升新主并切换应用路由;切换后的数据差异、旧主重加入和业务重试必须单独处理。

面试口语版(约 80 秒)

“我会把故障切换分成检测、隔离、选主、切流和校验。先用多点探测确认主库不可用,防止只是应用到数据库的网络分区。关键是 fencing:在提升从库之前通过代理撤掉旧主写路由、关写权限或用可靠的基础设施隔离旧主;做不到隔离时宁可暂时停写,避免双主。然后核对候选副本已执行的 GTID,以及 relay log 中完整可恢复的事务,优先选择数据最完整且健康的副本;必要时先回放、核对,再开放写入并更新代理和连接池。异步复制下旧主可能有已提交但未复制的事务,所以要估计 RPO、核对订单或流水,不能承诺零丢失。最后旧主恢复时先隔离检查差异,按新主重建或谨慎补偿,不可直接变回主库。方案上可人工或自动编排异步复制,也可用 Group Replication/InnoDB Cluster 与 Router,但都要按拓扑、事务一致性设置和业务目标演练。”

切换顺序与故障分支

flowchart TD
  A[多点检测主库故障] --> B[停止新写与隔离旧主]
  B --> C[核对已执行 GTID 和完整可恢复事务]
  C --> D[回放完整事务并再次核对]
  D --> E[提升新主、切换代理和连接池]
  E --> F[验证写入、读一致性和数据差异]
  F --> G[旧主隔离检查后重建或补偿]
方案适用场景主要边界
人工切换异步复制低频故障、允许较长 RTO依赖值班响应和成熟操作手册
自动编排异步复制需要较短 RTO必须可靠地判故障、fencing、选择候选并处理分叉
Group Replication 单主模式 + Router接受组复制的部署和仲裁成本组内可自动选新主;客户端仍需 Router/代理/应用切路由,读一致性取决于配置与积压

例子与失败分支: 主库 A 提交订单 T100 后断电,从库 B 只收到 T99。即使 B 很快被提升,新主也不会凭空拥有 T100;客户端若重试创建订单,需用业务幂等键判断新主状态,旧主 A 恢复后比对并决定补录或业务补偿。若 A 实际只是网络分区但还在接受写入,未隔离就提升 B 会产生分叉,事后合并代价更高。

关键细节与常见误区

  • GTID 便于识别和比较事务,但“启用 GTID”本身不保证候选副本拥有旧主全部已提交事务。Retrieved_Gtid_Set 在事务内容尚未传完时也可能包含其 GTID,不能仅凭它决定提升;要核对完整可恢复的 relay log、回放后的 Executed_Gtid_Set、GTID 集差集与 errant transaction。
  • 半同步只等副本收到并记录事件,不等同于副本已执行;若等待超时还可能退回异步。RPO 需要按当时实际模式和切换候选评估。
  • MySQL Group Replication 单主模式可自动选主,但若新主还有待回放事务,立即开放读可能看到旧数据;需按 group_replication_consistency 等设置验证切换期间语义。
  • RTO 是恢复服务的时间,RPO 是可接受的数据丢失窗口;缩短 RTO 常会增加自动化复杂度,降低 RPO 则需更强的复制/确认策略。

面试官递进追问

1. 如何区分主库宕机与应用到主库的网络分区?

参考答案: 一次连接超时只能证明某条访问路径失败。我会从不同故障域同时检查 TCP/SQL 连通性、主库主机和进程状态、代理探测及副本复制连接:若只有一组应用失败,而其他探测点和副本仍能访问主库,优先判断网络分区;若主机电源或进程退出有明确证据,才支持宕机判断。探测仍可能不完整,因此是否提升新主不能只依赖多数探测失败,更要确保旧主已失去写能力。可通过可靠的电源/网络隔离或写入租约机制执行 fencing,并撤销路由、处理已有连接;无法确认隔离时先停写,防止网络恢复后出现双主。

2. 两个从库接收与执行 GTID 不一致,你怎样选择候选并控制数据丢失?

参考答案: 先完成旧主隔离并保留各候选的 GTID 与日志证据,再比较合法已执行事务集合是否存在包含关系,检查接收日志中尚未回放但完整可恢复的事务。例如 A 执行到 T100,B 执行到 T98 但完整收到 T101,B 回放并验证后可能更完整;若 T101 只有 GTID 头部,则不能据此选它。双方各有对方缺少的事务时,应排查异常本地写入和复制来源,不能直接把集合合并当成数据一致。优先补齐合法事务、回放验证后再开放新主,无法补齐时按 RPO 决定等待还是接受损失;半同步场景还要核对实际 ACK 副本与是否曾退回异步。官方依据:GTID 故障切换。

3. 旧主恢复后发现多了三笔新主没有的订单,你会如何处理并防止再次分叉?

参考答案: 先保持旧主隔离,保存数据和 binlog,不能直接恢复为可写主库或覆盖新主。用 GTID 差集、订单业务幂等键和支付/库存流水确认这三笔是切换前已成功提交但未复制的订单,还是网络分区期间产生的分叉写入,同时核对新主上是否已有重试生成的等价订单。合法缺单通过可审计、幂等的业务补录或补偿流程处理;有支付、库存或编号冲突的记录逐笔协调,不能盲目重放整段旧主日志。对账完成后以新主权威数据重建旧主为只读副本。后续加强可靠 fencing、写路由与旧连接回收、候选完整性校验,并演练网络分区和旧主重加入流程。

自测与学习清单

  • 用“主库真宕机”和“主库被网络隔离但仍可写”两个场景演练切换顺序。
  • 列出每一步需要的证据:健康检查、fencing 结果、GTID 差集、回放状态、应用错误率。
  • 记录切换 RTO、实际 RPO、重复写请求、旧主重加入与补偿结果,复盘自动化缺口。

延伸阅读

参考资料

维护与整理 · Yihui在 GitHub 上编辑

继续阅读

浏览全部文章

SQL与NoSQL有什么区别?MySQL和MongoDB如何选型?实际项目中如何选择?

日期:2026-09-27 标签:#面试 #MySQL #数据库 难度:简单 来源:牛面 MySQL 题库 答案说明:独立整理(站内题目标记为 VIP,未读取会员答案) 一句话答案 MySQL与MongoDB的主要差异在数据模型、事务边界、查询方式和模式演化;按业务访问模式和一致性需求选型。 面试口语版(约 60…

阅读全文