MySQL 高可用与容灾
复制是高可用的基础组件,但复制本身不等于高可用。完整方案还需要故障检测、选主、流量切换、防脑裂、数据补偿和演练。
先定义 RPO 与 RTO
- RPO:可接受丢失多少数据,例如 0 秒、30 秒或 5 分钟。
- RTO:故障后可接受多久恢复服务。
没有 RPO/RTO 就无法判断异步、半同步、多机房和备份投入是否合理。
复制模式
| 模式 | 提交语义 | 优点 | 风险与代价 |
|---|---|---|---|
| 异步复制 | 主库本地提交即可返回 | 延迟低、吞吐好 | 主库突然丢失时,未传到副本的事务可能丢失 |
| 半同步复制 | 至少一个副本确认收到日志后返回 | 缩小数据丢失窗口 | 网络或副本异常会增加提交延迟,并可能退化 |
| Group Replication | 组通信与一致性协议协调成员 | 自动成员管理、支持单主/多主模式 | 部署与故障语义更复杂,仍需理解一致性和路由 |
半同步通常确认的是副本收到并写入 relay log,不等于业务已经能从副本读取到该事务。
故障切换流程
mermaid
flowchart TD
A["监控发现主库异常"] --> B["多信号确认并隔离旧主"]
B --> C["比较候选副本 GTID 与延迟"]
C --> D["选择数据最完整的健康副本"]
D --> E["提升为新主并更新拓扑"]
E --> F["切换代理、DNS 或服务发现"]
F --> G["验证读写、复制和业务指标"]
G --> H["修复旧主并作为副本重新加入"]最危险的问题是脑裂:旧主实际仍能写,新主也开始接收写入。必须通过 fencing(隔离电源、网络、存储或撤销写入口)保证同一时刻只有一个可写主节点。
GTID
GTID 为每个已提交事务提供全局标识,能帮助判断事务集合、切换复制来源和跳过已执行事务。它简化拓扑管理,但不能自动解决:
- 业务写入幂等。
- 已丢失事务恢复。
- 冲突数据合并。
- 故障检测误判。
- 新旧主同时写入。
读写分离的一致性
异步副本可能读不到刚写入的数据。常见策略:
- 强一致读固定走主库。
- 写后一定时间或同一会话内读主库。
- 等待特定 GTID 已在副本执行后再读。
- 根据复制延迟动态摘除副本。
- 业务使用版本号或状态机容忍旧值。
不能简单用固定 sleep 保证同步,它既不可靠又增加延迟。
跨机房与多地域
距离会增加网络 RTT,同步确认会直接拉高事务提交延迟。设计要权衡:
- 单地域同步、多地域异步的 RPO。
- 地域级故障切换是否允许人工确认。
- DNS、代理、连接池缓存和客户端重连时间。
- 数据合规和跨境限制。
- 演练时如何避免真实双写。
高可用不等于备份
误删、错误 DDL、应用 Bug、勒索和逻辑损坏可能快速复制到所有副本。必须独立保留全量备份、binlog、异地副本或不可变副本,并定期做恢复演练。
演练清单
- 主库进程崩溃、主机宕机、网络分区分别如何检测。
- 切换是否先隔离旧主,谁有权限触发。
- 候选副本落后多少,预计丢失哪些事务。
- 应用连接池多久重连,长事务如何处理。
- 新主容量是否足够,其他副本如何重建复制。
- 是否能在目标 RTO 内完成,最终 RPO 如何核对。
- 切回还是继续运行,旧主数据如何校验和处理。
高频面试题
主库宕机后是不是直接提升从库
不能。先确认故障并隔离旧主,防止脑裂;比较候选副本的健康、延迟和 GTID 集合,选择数据最完整者;提升后切换流量并验证读写,再重建复制。还要根据异步或半同步语义说明可能的数据丢失窗口。
半同步能保证不丢数据吗
不能无条件保证。它缩小主库独自持有事务的窗口,但确认点、超时退化、网络分区、副本是否落盘以及故障切换选择都会影响结果。严格目标要由具体配置、拓扑和故障模型证明。
