数据库挂了,业务就挂了——高可用(HA)的目标是把"数据库不可用"的概率压到最低,并在不可避免的故障发生时自动、快速、无感知地完成切换。但高可用不是"买几台机器做主从"这么简单:主从复制只是基础,真正难的是故障检测(怎么判断主库真挂了)、自动切换(谁能成为新主)、脑裂防护(双主都以为自己活着)、数据一致性(切换不能丢数据)。本指南系统梳理数据库高可用的完整工程:复制方案选型、故障检测与切换协议、MHA/Orchestrator/Patroni 等工具、脑裂与防脑裂设计、RTO/RPO 指标量化,以及云原生托管方案的实践。
一、高可用目标与量化
1.1 RTO / RPO 的定义
RPO(Recovery Point Objective):可接受的最大数据丢失量
· RPO=0 :切换零丢失(需要同步复制/共享存储)
· RPO<5s :异步复制可接受小丢失
· RPO=5m :定时备份场景
RTO(Recovery Time Objective):可接受的最大停机时长
· RTO<30s:自动切换(秒级-分钟级)
· RTO<5m :脚本化手动切换
· RTO=1h :依赖重建/备份恢复
目标设定决定架构:
RPO=0 + RTO=0 → 共享存储 + 集群(昂贵、复杂)
RPO<1s + RTO<30s → 半同步 + 自动切换(绝大多数业务)
RPO=5m + RTO=5m → 备份恢复 + 预演(低成本)
ℹ️ 核心洞察:高可用设计的一切取舍,都是 RPO 与 RTO 的平衡——同步复制保 RPO 但牺牲可用性(主从都慢),异步复制保可用性但可能丢数据。先定业务目标,再选架构。
1.2 高可用等级
L0:单实例 + 备份 (宕机 = 停机恢复)
L1:主从异步复制 (故障切换靠人工,可能丢数据)
L2:半同步 + 自动切换 (秒级 RTO,毫秒级 RPO)
L3:强一致复制/分布式 (RPO≈0,需集群协议)
L4:多可用区/多地域 (区域级容灾)
二、复制方案选型
2.1 异步 vs 半同步 vs 同步
| 方案 | 原理 | RPO | 可用性代价 |
|---|---|---|---|
| 异步 | 主库先提交,binlog 异步送达 | 可能丢几秒 | 无 |
| 半同步 | 主库等至少一个从库 ack 才提交 | ≈0(正常) | 从库慢则主库变慢 |
| 同步 | 所有从库 ack 才提交 | 0 | 任一从库慢=全慢 |
-- MySQL 半同步配置(5.7+ 半同步增强版)
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 3000; -- 3s 超时降级异步
-- 从库
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
⚠️ 关键权衡:半同步在"从库 ack 超时"后降级为异步——这保证了主库可用性,但也意味着降级期间可能丢数据。半同步减少风险、不根除风险。
2.2 PostgreSQL 同步/异步
-- PG 同步流复制(synchronous_standby_names)
ALTER SYSTEM SET synchronous_standby_names = '2 (standby1, standby2)';
-- "2" 表示至少 2 个从库确认
-- 无从库确认时主库阻塞(强一致牺牲可用性)
2.3 多从 + 级联拓扑
常见拓扑:
· 一主一从(最小 HA)
· 一主多从(读扩展 + 容灾)
· 一主一备一从(备=切换目标,从=读)
· 级联(从库再挂从库,节省主库 binlog 负担)
· 主从主(环形,慎用,容易脑裂)
建议:
· 切换目标建议选"半同步从库"(数据最新)
· 一个从库专门做报表/备份(可延迟同步)
三、故障检测:怎么判断"主库挂了"
3.1 检测维度
故障检测信号:
· 网络层:ping / TCP 探活
· 协议层:SELECT 1 / SHOW STATUS(主库进程活着但挂起?)
· 应用层:写请求持续失败
· 复制层:从库与主库失去心跳
判断原则:
· 不能只看"一次探活失败"就切换(误判)
· 需连续 N 次失败 / 超时窗口确认
· 多个观察者(如 Orchestrator 三节点)多数派判定
→ 避免"单点误判触发灾难性切换"
3.2 探活与心跳
# heartbeat_probe.py — 连续失败判定故障
FAIL_THRESHOLD = 3
WINDOW = 5 # seconds
def detect_master_failure(probe):
failures = 0
while True:
if probe.ok():
failures = 0
else:
failures += 1
if failures >= FAIL_THRESHOLD:
return True # 连续 3 次失败 → 判定故障
time.sleep(WINDOW / FAIL_THRESHOLD)
四、自动切换的核心问题
4.1 切换流程
完整故障切换:
1. 故障检测(多数派确认主库不可用)
2. 选举新主(选择数据最新的从库)
3. 提升新主(激活写入)
4. 重定向流量(VIP / DNS / 应用配置)
5. 其余从库 re-point 到新主
6. 通知 / 记录 / 复盘
每步都可能出问题,HA 工具就是把这些自动化 + 加防护
4.2 脑裂(Split-Brain)问题
脑裂场景:
· 主库与工具/观察者网络断开,但主库进程还活着
· 观察者判定主库死亡 → 提升从库为新主
· 旧主恢复网络 → 两个"主库"同时在写 → 数据分叉
防脑裂手段:
· 多数派仲裁(quorum):切换需大多数节点同意
· STONITH(fencing):确认旧主无法再写(杀进程/断网)
· 独立仲裁节点(如 ZooKeeper/etcd/Orchestrator 多节点)
· 心跳隔离(旧主被隔离时才允许新主接管)
⚠️ 核心原则:宁可停机,不可脑裂。双主分叉比短暂不可用严重得多——它让数据无法合并、后续全部基于错误基线。防脑裂的第一道防线是"fencing":提升新主前确保旧主真的不能再写。
4.3 数据一致性检查(切换前)
切换前评估各从库数据位置:
· 对比各从库 binlog 位点/GTID
· 选"最新"的从库作为新主(丢失最小)
· 记录切换时点(供事后对账)
· 若差异过大,考虑阻止切换(人工决策)
五、MySQL HA 工具:MHA 与 Orchestrator
5.1 MHA(经典方案)
MHA(Master High Availability):
· 检测主库故障
· 找出数据最新的从库
· 补齐其余从库的 binlog 差距
· 提升新主 + 重配置其他从库
· 切换通常 30s-1min
局限:
· 无内置多数派/仲裁(单 Manager 单点)
· 维护已放缓,社区转向 Orchestrator
# mha.conf 关键配置
[server default]
manager_log=/var/log/masterha/manager.log
master_binlog_dir=/var/lib/mysql
user=root
password=***
manager_workdir=/var/log/masterha
[server1]
hostname=db-master
[server2]
hostname=db-standby
[server3]
hostname=db-read-1
5.2 Orchestrator(Raft 仲裁 + 拓扑管理)
Orchestrator 特性:
· 拓扑可视化(发现/接管/拖挂)
· 自动故障检测 + 恢复(Raft 多数派仲裁)
· 优雅切换(手动/计划)
· 防脑裂:三节点 Raft 集群,只有多数派可切换
· 可探测 MySQL 从属关系(GTID)
更适合现代 MySQL HA:
· 三节点 Orchestrator + 半同步从库
· RTO 通常 <30s
# orchestrator CLI 常见命令
orchestrator -c topology -i db-master # 查看拓扑
orchestrator -c discover -i db-master
orchestrator -c graceful-master-takeover -i db-master # 计划切换
orchestrator -c recover -i db-master # 手动恢复
5.3 MySQL Group Replication / InnoDB Cluster
MySQL InnoDB Cluster(官方 HA):
· Group Replication:Paxos 协议,多数派写入
· MySQL Router:自动路由读写
· 单主 + 多读;自动故障转移(秒级)
· RPO≈0(组复制多数派确认)
适用:新部署 / 对官方方案友好 / 接受组复制约束
六、PostgreSQL HA:Patroni 与 Repmgr
6.1 Patroni(Kubernetes 时代主流)
Patroni = PG 高可用控制器 + 分布式共识(etcd/ZooKeeper/Consul)
· leader 选举:当前主库持有 lease
· 故障:多数派认可后 promote 备库
· 自动配置同步/动态参数
· 与 K8s Operator 深度集成(CloudNativePG/Zalando)
配置要点:
· 每个 PG 实例跑 Patroni
· 共享 etcd 集群做决策源
· synchronous_mode 可选(RPO=0)
# patroni.yml
scope: postgres-ha
namespace: /pg/
name: pg-0
restapi:
listen: 0.0.0.0:8008
connect_address: pg-0:8008
etcd:
hosts: [etcd-0:2379, etcd-1:2379, etcd-2:2379]
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
postgresql:
use_pg_rewind: true # 旧主回归用 rewind 对齐
use_slots: true
parameters:
synchronous_mode: "off" # 或 on 换取 RPO=0
postgresql:
listen: 0.0.0.0:5432
data_dir: /var/lib/postgresql/data
6.2 Repmgr
Repmgr(轻量 PG HA):
· 监控复制 + 故障检测
· failover 自动/手动
· 无外部依赖(较 Patroni 简单)
· 防脑裂:repmgr 用 witness 节点仲裁(可选)
适用:简单集群,不想引入 etcd
七、读写分离 + VIP 与流量切换
7.1 VIP 漂移
VIP(虚拟 IP)+ keepalived:
· 主库持有 VIP
· 切换时 VIP 漂移到新主
· 应用连 VIP,无感知
缺点:
· 单子网内可用,跨 AZ/跨机房需 DNS/负载均衡
· keepalived 本身需防脑裂配置(VRRP 优先 + 主从约束)
7.2 连接层切换
现代方案:
· MySQL Router(InnoDB Cluster 官方)
· ProxySQL(读写分离 + 故障路由)
· DNS + TTL(切 DNS 有缓存延迟)
· 应用连接池 + 探活重连
→ 建议:ProxySQL/Router 负责"切换对应用透明"
# proxysql.cnf — 主从分组 + 故障切换
mysql_replication_hostgroups = 0,1
mysql_servers:
hostname=db-master port=3306 hostgroup=0 # 写组
hostname=db-standby port=3306 hostgroup=1 # 读组
mysql_monitor:
monitor_username=monitor
monitor_password=***
monitor_ping_interval=5000
# 主库故障 → ProxySQL 自动把写组切到 standby
八、云原生与托管 HA
8.1 云数据库(AWS/Azure/GCP/阿里云)
托管数据库自带 HA:
· 自动故障切换(RDS Multi-AZ、Aurora)
· 多 AZ 部署、自动备份 + PITR
· RTO 通常 1-2 分钟(托管 SLA)
· 无需自建 MHA/Orchestrator
优点:省运维、SLA 保障、跨 AZ 容灾
注意:切换行为由云厂商定义(不透明窗口),需验证
8.2 K8s 上的数据库
· Zalando Postgres Operator / CloudNativePG(PG on K8s)
· KubeBlocks(多数据库统一 Operator)
· Vitess(MySQL 分布式 + K8s 原生)
→ 把 HA 变成"声明式配置",自动恢复 + 滚动切换
九、演练与验证
9.1 故障演练
必须演练的场景:
· 主库进程挂掉(kill -9)
· 主库网络断开(防火墙规则)
· 主库所在节点宕机(整机)
· 半同步从库全部失效(降级路径)
· 仲裁节点故障(etcd/Orchestrator 部分可用)
· 脑裂模拟(旧主恢复网络)
演练验证:
· RTO 是否达标(记录切换耗时)
· RPO 是否可接受(对比切换后数据)
· 应用是否自动重连(连接池/Proxy 行为)
· 监控告警是否准确
9.2 监控指标
# HA 健康指标
master_role_current{instance} # 谁是当前主库
replica_seconds_behind_master # 从库延迟
semi_sync_status # 半同步是否生效
ha_failover_total # 切换次数
ha_last_failover_duration # 最近切换耗时
# 告警:从库延迟超阈值、半同步降级、仲裁不可用
9.3 常见坑速查
| 坑 | 后果 | 对策 |
|---|---|---|
| 单 Manager 无仲裁 | 误判切换 | Orchestrator Raft |
| 无 fencing | 脑裂双写 | STONITH/隔离 |
| 全异步 | 切换丢数据 | 半同步 + 记录位点 |
| 未演练 | 切换时才暴露问题 | 定期故障演练 |
| VIP 跨网段 | 切换失败 | 用连接层路由 |
| 从库数据旧被提升 | 大量丢数 | 选最新从库 + 对账 |
总结:数据库 HA 决策表
| 环节 | 关键动作 |
|---|---|
| 目标 | 先定 RTO/RPO,再选架构 |
| 复制 | 半同步优先(RPO≈0 且可用性可接受) |
| 检测 | 连续探活失败 + 多数派仲裁 |
| 切换 | 选最新从库 + fencing 防脑裂 |
| 工具 | MySQL: Orchestrator;PG: Patroni |
| 路由 | VIP / ProxySQL / Router 透明切换 |
| 演练 | 定期故障演练验证 RTO/RPO |
| 演进 | 托管/K8s Operator 省运维 |
数据库高可用的本质,是把"主库挂了"从事故变成例行流程——检测、选举、提升、隔离、重定向,全部自动化并加上脑裂防护。它不是你买来的某个工具,而是"目标(RTO/RPO)→ 复制方案 → 检测 → 切换协议 → 防脑裂 → 流量切换 → 演练验证"的一整套工程闭环。落地守住四件事:半同步减少丢失、多数派仲裁防误判、fencing 防脑裂、演练验证 RTO/RPO。把 HA 当作产品来运营(有指标、有演练、有复盘),数据库才能真正成为业务的"可靠底座"而非"最大风险源"。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。