这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

高可用

Pigsty 使用 Patroni + Etcd + HAProxy 实现 PostgreSQL 的故障自愈高可用架构。

如果您只有一分钟,请记住这张图:

flowchart TB
    subgraph Normal["✅ 正常运行"]
        Client1["客户端"] --> HAProxy1["HAProxy"]
        HAProxy1 --> Primary1["🟢 Primary<br/>读写"]
        Primary1 --> |流复制| Replica1["🔵 Replica<br/>只读"]
    end

    Normal --> |主库故障| Failover

    subgraph Failover["⚡ 自动切换 (30秒内)"]
        F1["1️⃣ Patroni 检测到主库心跳丢失"]
        F2["2️⃣ Etcd 释放领导者锁,触发选举"]
        F3["3️⃣ 数据最完整的从库提升为新主库"]
        F4["4️⃣ HAProxy 自动路由到新主库"]
        F1 --> F2 --> F3 --> F4
    end

    Failover --> Recovered

    subgraph Recovered["✅ 服务恢复"]
        Client2["客户端"] --> HAProxy2["HAProxy"]
        HAProxy2 --> Primary2["🟢 新 Primary<br/>读写"]
        Note["应用无需修改连接串<br/>无需重启"]
    end

核心价值:硬件故障?自动切换。主库挂了?30 秒恢复。从库挂了?零感知切换。睡个好觉,让系统自己照顾自己。


本章内容

章节 说明 核心问题
高可用原理 Patroni + Etcd + HAProxy 协同工作机制 故障自愈是如何实现的?
故障切换 主库故障、从库故障时的检测与切换流程 故障发生后会怎样?
利弊权衡 RTO/RPO 参数调优与可用性/一致性的权衡 如何根据业务需求配置?

高可用解决什么问题?

问题:单点故障

传统单机 PostgreSQL 面临一个残酷的现实:

flowchart LR
    App["应用"] --> PG["PostgreSQL"]
    PG --> Data["数据"]

    PG --> |硬盘故障?<br/>内存错误?<br/>主板烧毁?<br/>机房断电?| Disaster

    subgraph Disaster["💀 灾难后果"]
        D1["服务完全中断"]
        D2["数据可能丢失"]
        D3["凌晨 3 点被叫醒"]
        D4["手忙脚乱恢复"]
    end

解决方案:高可用集群

Pigsty 的高可用方案让这一切自动化:

flowchart TB
    App["应用"] --> HAProxy["HAProxy"]
    HAProxy --> Primary["🟢 Primary"]
    Primary --> |流复制| Replica1["🔵 Replica1"]
    Primary --> |流复制| Replica2["🔵 Replica2"]

    Primary --> |数据| D1["数据"]
    Replica1 --> |副本| D2["数据副本"]
    Replica2 --> |副本| D3["数据副本"]

    Primary -. "Primary 故障?" .-> Failover["⚡ 自动切换<br/>30 秒内完成"]
    Failover --> Replica1
    Replica1 --> |提升为| NewPrimary["🟢 新 Primary"]

    subgraph Result["✅ 结果"]
        R1["服务短暂闪断后恢复"]
        R2["数据安全(有副本)"]
        R3["无需人工干预"]
        R4["继续睡觉"]
    end

高可用的价值

故障自愈

  • RTO < 30s:主库故障后 30 秒内自动恢复服务
  • RPO < 1MB:默认配置下最多丢失约 1MB 未同步数据(可配置为 0)
  • 零人工干预:从检测到恢复全自动完成

滚动维护

  • 无需停服:通过主动切换(Switchover)可在不停服情况下维护任意节点
  • 升级便利:滚动升级 PostgreSQL、操作系统、硬件无需业务配合
  • 最小窗口:单次切换对业务影响仅几秒闪断

资源利用

  • 从库可用:从库承载只读查询,分担主库压力
  • 读写分离:通过不同端口自动路由读写流量
  • 弹性扩展:增加从库即可线性提升只读吞吐

运维友好

  • 硬件故障不紧急:自动恢复后可以第二天再处理故障节点
  • 监控完善:复制延迟、连接数、健康状态一目了然
  • 操作简单:一条命令完成切换、扩容、缩容

核心指标

默认配置

指标 默认值 说明
RTO(恢复时间目标) ≈ 30s 主库故障到新主库接管的时间
RPO(恢复点目标) < 1MB 故障时可能丢失的最大数据量
从库故障 RTO ≈ 0 只读请求立即路由到其他从库
从库故障 RPO = 0 从库故障不丢失数据

可选配置

使用 crit.yml 关键业务模板,可确保:

指标 配置值 说明
RPO = 0 同步复制,零数据丢失
代价 延迟增加 写入需等待从库确认

配置策略对比

根据您的业务需求,选择合适的配置策略:

配置策略 RTO RPO 适用场景
单机 + 什么也不做 ❌ 无法恢复 ❌ 数据全丢 绝不推荐
单机 + 定期备份 ⚠️ 数小时 ⚠️ 丢失数小时数据 开发测试
单机 + 备份 + WAL 归档 ⚠️ 数小时 ✅ 丢失约数十 MB 非关键业务
主从 + 自动切换 ✅ < 1 分钟 ✅ 丢失约百 KB 大多数生产环境
主从 + 同步复制 ✅ < 1 分钟 零丢失 金融/关键业务

高可用的代价

任何技术选择都有代价,高可用也不例外:

资源成本

单机部署:     1 台服务器
高可用部署:   至少 3 台服务器(1 主 2 从 + ETCD)

资源消耗增加约 3 倍,但获得了:
✅ 99.999% 可用性
✅ 数据安全性
✅ 运维便利性

复杂度

  • 基础设施依赖:需要部署 Etcd 集群(Pigsty 已自动处理)
  • 学习成本:需要理解 Patroni、HAProxy 等组件
  • 调试难度:分布式系统问题定位比单机复杂

性能开销

  • 异步复制:几乎无开销
  • 同步复制:写入延迟增加(等待从库确认)

高可用的局限性

高可用不是万能的,它无法解决以下问题:

逻辑错误

-- 这条命令会立即复制到所有从库!
DROP TABLE users;

-- 高可用无法帮你恢复,因为:
-- 主库执行 → 从库复制 → 所有节点都删除了

解决方案:使用 时间点恢复(PITR) 回退到误操作前

软件缺陷

应用 Bug 导致的数据污染会复制到所有从库。

解决方案:配合 延迟集群 提供回退窗口

机房级灾难

如果整个机房断电或网络中断,所有节点都不可用。

解决方案:跨机房/跨区域部署,或使用云厂商多可用区


生产验证

许多大型组织与核心机构已经在生产环境中长时间使用 Pigsty 高可用方案:

指标 数据
最大部署规模 25,000 CPU 核心,220+ PostgreSQL 实例
单实例最大规格 64 核 / 512GB 内存 / 3TB NVMe SSD
运行时间 5 年以上持续运行
硬件故障次数 数十次(磁盘、内存、主板、电源)
数据丢失
总体可用性 > 99.999%

快速配置

最小高可用集群

# pigsty.yml - 三节点高可用集群
pg-test:
  hosts:
    10.10.10.11: { pg_seq: 1, pg_role: primary }
    10.10.10.12: { pg_seq: 2, pg_role: replica }
    10.10.10.13: { pg_seq: 3, pg_role: replica }
  vars:
    pg_cluster: pg-test
# 一条命令部署
bin/pgsql-add pg-test

关键业务配置

# 启用同步复制,确保零数据丢失
pg-critical:
  hosts:
    10.10.10.21: { pg_seq: 1, pg_role: primary }
    10.10.10.22: { pg_seq: 2, pg_role: replica }
    10.10.10.23: { pg_seq: 3, pg_role: replica }
  vars:
    pg_cluster: pg-critical
    pg_conf: crit.yml    # 关键业务模板
    pg_rpo: 0            # 零数据丢失

接下来

了解高可用的工作原理:

相关话题:

1 - 高可用原理

Patroni、Etcd、HAProxy 如何协同工作,实现 PostgreSQL 集群的故障自愈。

Pigsty 的高可用架构基于三个核心组件的协同工作:Patroni 负责管理 PostgreSQL 进程与故障切换,Etcd 提供分布式共识与领导者选举,HAProxy 负责流量路由与健康检查。


架构概览

                          ┌─────────────────────────────────────────────────────────┐
                          │                        Etcd 集群                         │
                          │   ┌─────────┐    ┌─────────┐    ┌─────────┐            │
                          │   │  etcd1  │◄──►│  etcd2  │◄──►│  etcd3  │            │
                          │   └────▲────┘    └────▲────┘    └────▲────┘            │
                          └────────┼──────────────┼──────────────┼─────────────────┘
                                   │              │              │
              ┌────────────────────┼──────────────┼──────────────┼────────────────────┐
              │                    │              │              │                    │
              │              ┌─────┴─────┐  ┌─────┴─────┐  ┌─────┴─────┐             │
              │              │  Patroni  │  │  Patroni  │  │  Patroni  │             │
              │              └─────┬─────┘  └─────┬─────┘  └─────┬─────┘             │
              │                    │              │              │                    │
              │              ┌─────┴─────┐  ┌─────┴─────┐  ┌─────┴─────┐             │
              │              │PostgreSQL │  │PostgreSQL │  │PostgreSQL │             │
              │              │  Primary  │  │  Replica  │  │  Replica  │             │
              │              └─────┬─────┘  └─────┬─────┘  └─────┬─────┘             │
              │                    │              │              │                    │
              │                    └──────────────┼──────────────┘                    │
              │                                   │                                   │
              │                          流式复制 (WAL)                               │
              │                                                                       │
              │         ┌─────────────────────────────────────────────────┐          │
              │         │                   HAProxy                        │          │
              │         │  ┌──────────┐  ┌──────────┐  ┌──────────┐       │          │
              │         │  │Port 5433 │  │Port 5434 │  │Port 5436 │  ...  │          │
              │         │  │ Primary  │  │ Replica  │  │ Default  │       │          │
              │         │  └──────────┘  └──────────┘  └──────────┘       │          │
              │         └─────────────────────────────────────────────────┘          │
              │                                                                       │
              └───────────────────────────────────────────────────────────────────────┘
                                               PostgreSQL 集群

组件职责

PostgreSQL

PostgreSQL 是整个系统的核心,提供关系型数据库服务。

  • 使用标准流复制(Streaming Replication)搭建物理从库
  • 主库接受读写请求,从库接受只读请求
  • 在多个节点上部署相同集群名的实例,自动形成主从复制拓扑

Patroni

Patroni 是高可用的控制平面,负责管理 PostgreSQL 进程的完整生命周期。

  • 将 PostgreSQL 作为子进程启动和管理
  • 监控 PostgreSQL 的健康状态
  • 与 Etcd 交互进行领导者选举和配置存储
  • 提供 REST API(默认端口 8008)供健康检查使用
  • 执行自动故障转移(Failover)和主动切换(Switchover)

Etcd

Etcd 是分布式配置存储(DCS),为高可用提供共识基础。

  • 存储集群的元数据和配置信息
  • 提供分布式锁机制实现领导者选举
  • 集群领导者持有租约,心跳续期
  • 当领导者失联超时,触发新一轮选举

HAProxy

HAProxy 是流量入口,负责服务路由和负载均衡。

  • 通过 Patroni REST API 进行健康检查
  • 根据实例角色(Primary/Replica)分发流量
  • 不同端口对应不同服务:5433(读写)、5434(只读)、5436(直连)
  • 故障切换对应用透明,无需修改连接串

工作流程

正常运行时

  1. Patroni 定期向 Etcd 发送心跳,续期领导者租约
  2. Patroni 监控本地 PostgreSQL 健康状态
  3. Patroni 通过 REST API 对外暴露实例角色
  4. HAProxy 周期性检查 Patroni API,维护后端池
  5. 客户端流量通过 HAProxy 路由到正确的实例

从库故障时

  1. HAProxy 检测到从库 Patroni API 不可达
  2. HAProxy 将该从库从后端池中移除
  3. 只读流量自动路由到其他健康从库
  4. 如果所有从库都故障,只读流量回退到主库
  5. 影响:部分只读查询闪断,立即恢复

主库故障时

  1. 主库 Patroni 停止向 Etcd 发送心跳
  2. Etcd 租约过期(默认 10 秒),释放领导者锁
  3. 存活的从库 Patroni 竞争领导者锁
  4. 数据最完整的从库(LSN 最高)胜出
  5. 胜选从库提升为新主库,其他从库重新指向新主库
  6. HAProxy 检测到角色变化,更新路由
  7. 影响:写服务短暂不可用(15s ~ 30s)

关键参数

参数 默认值 说明
pg_rto 30 恢复时间目标(秒),用于计算 Patroni TTL
pg_rpo 1048576 恢复点目标(字节),控制允许的数据丢失量
patroni_watchdog_mode off 看门狗模式,可启用硬件隔离防止脑裂
pg_conf oltp.yml 配置模板,crit.yml 启用同步复制

同步复制

默认配置使用异步复制,主库故障时可能丢失少量未复制的数据(通常 < 1MB)。

如果需要确保零数据丢失(RPO = 0),可以启用同步复制:

pg_conf: crit.yml          # 使用关键业务模板
pg_rpo: 0                   # 恢复点目标设为零

crit.yml 模板自动启用:

  • synchronous_mode: true:启用同步复制
  • synchronous_commit: on:事务提交需等待从库确认
  • 至少一个同步从库确认后事务才算提交成功

代价:写入延迟增加,吞吐量下降,从库故障会影响主库写入。

2 - 故障切换

主库故障、从库故障时的检测机制与切换流程详解。

Pigsty 的高可用系统支持两种切换模式:自动故障转移(Failover)和 主动切换(Switchover)。


故障检测

故障检测由 Patroni 和 Etcd 共同完成:

  1. 心跳机制:集群领导者(主库)持有 Etcd 租约,需要定期续期
  2. 租约超时:如果领导者在 TTL 时间内(默认 30s)未续期,租约释放
  3. 健康检查:HAProxy 通过 Patroni REST API 检测实例状态

检测时间线

0s      主库故障发生
        ↓
~10s    Etcd 租约即将过期,Patroni 最后一次心跳失败
        ↓
~15s    租约过期,领导者锁释放
        ↓
~20s    从库 Patroni 检测到领导者空缺,开始竞选
        ↓
~25s    新领导者当选,从库提升为主库
        ↓
~30s    HAProxy 检测到角色变化,流量切换完成

自动故障转移(Failover)

当主库因硬件故障、网络分区或进程崩溃而不可用时,系统自动执行故障转移。

流程详解

  1. 故障发生

    • 主库 Patroni 进程崩溃或网络不可达
    • Etcd 租约停止续期
  2. 领导者选举

    • 租约过期后,所有候选从库开始竞争领导者锁
    • 选举依据:数据完整性(LSN 位点最高者优先)
    • 如果 LSN 差距超过 pg_rpo 阈值,拒绝自动切换
  3. 主库提升

    • 胜选从库执行 pg_ctl promote 提升为主库
    • 其他从库重新配置,指向新主库
  4. 流量切换

    • Patroni REST API 返回新的角色信息
    • HAProxy 将写流量路由到新主库

对业务的影响

  • 写服务:不可用 15s ~ 30s,期间写请求失败或阻塞
  • 读服务:短暂闪断后恢复,从库继续提供只读服务

主动切换(Switchover)

在计划维护、滚动升级等场景下,可以主动将主库角色转移到指定从库。

执行方式

# 使用 patronictl 执行切换
patronictl -c /pg/bin/patroni.yml switchover

# 或使用 Pigsty 封装的命令
pg switchover <cluster>

流程详解

  1. 发起切换

    • 管理员通过 patronictl 或 API 发起 switchover 请求
    • 指定目标从库(可选)
  2. 优雅降级

    • 当前主库停止接受新的写入
    • 等待所有进行中的事务完成
    • 等待从库追上复制位点
  3. 角色交换

    • 原主库降级为从库
    • 目标从库提升为新主库
  4. 流量切换

    • HAProxy 检测角色变化,更新路由

对业务的影响

  • 写服务:短暂闪断(通常 < 5s)
  • 读服务:几乎无感知

从库故障

从库故障的处理相对简单:

  1. HAProxy 检测:健康检查失败,将从库从后端池移除
  2. 流量转移:只读请求路由到其他健康从库
  3. 降级处理:如果所有从库都故障,只读流量由主库承担

对业务的影响

  • 写服务:无影响
  • 读服务:正在该从库上执行的查询中断,后续请求自动路由到其他节点

脑裂防护

脑裂是分布式系统最危险的场景:网络分区导致出现多个主库。

Pigsty 的防护机制

  1. DCS 仲裁:领导者锁由 Etcd 集群统一管理,网络分区时只有能访问多数派的节点才能持有锁

  2. Failsafe 模式:Patroni 默认启用 failsafe_mode,当无法联系 DCS 时,主库降级为只读

  3. 看门狗(可选):启用硬件看门狗后,失去 DCS 联系的主库会被强制重启

    patroni_watchdog_mode: automatic  # 或 required
    
  4. VIP 漂移:vip-manager 从 Etcd 获取领导者信息,只有真正的主库才能绑定 VIP


故障恢复

原主库恢复

当故障的原主库重新上线时:

  1. 角色降级:原主库发现已有新领导者,自动降级为从库
  2. 数据同步:如果数据差异在 pg_rpo 范围内,使用 pg_rewind 快速同步
  3. 重新加入:作为从库重新加入集群

手动干预场景

以下情况可能需要人工干预:

  • 数据差异超过 pg_rpo 阈值
  • 时间线分叉严重
  • 需要从备份恢复数据
# 查看集群状态
patronictl -c /pg/bin/patroni.yml list

# 手动重新初始化成员
patronictl -c /pg/bin/patroni.yml reinit <cluster> <member>

最佳实践

  1. 至少三节点:生产环境建议 1 主 2 从,确保故障切换有足够候选

  2. 跨机架/机房部署:避免单点故障影响整个集群

  3. 监控告警:配置复制延迟、连接数等告警

  4. 定期演练:通过 switchover 验证故障切换流程

  5. 备份策略:HA 不能替代备份,配合 PITR 应对逻辑错误

3 - 利弊权衡

RTO/RPO 参数调优,以及可用性与一致性之间的权衡取舍。

高可用系统设计的核心是在 可用性一致性 之间找到平衡。Pigsty 通过 RTO 和 RPO 两个关键参数,让您根据业务需求灵活调整这一权衡。


核心指标

RTO(恢复时间目标)

Recovery Time Objective:从故障发生到服务恢复的最长时间。

  • 影响因素:故障检测时间、领导者选举时间、流量切换时间
  • 默认值:30 秒
  • 调整范围:15s ~ 60s(不建议更低或更高)

RPO(恢复点目标)

Recovery Point Objective:可接受的最大数据丢失量。

  • 影响因素:复制延迟、是否启用同步复制
  • 默认值:1MB(异步复制)
  • 调整范围:0(同步复制)~ 无限大

参数配置

pg_rto

pg_rto: 30  # 恢复时间目标,单位:秒

此参数用于计算 Patroni 的 TTL 值:

  • 减小 RTO:更快检测故障,更快切换,但增加误报风险
  • 增大 RTO:减少因网络抖动触发的误切换,但故障恢复时间更长

建议

网络环境 推荐值 说明
高质量内网 15-20s 网络稳定,可容忍较低的误报风险
一般内网 30s 默认值,平衡误报与恢复时间
跨机房/云环境 45-60s 网络延迟较高,避免频繁误切换

pg_rpo

pg_rpo: 1048576  # 恢复点目标,单位:字节(1MB)

此参数控制自动故障切换时允许的最大数据丢失:

  • 当候选从库落后主库超过此值时,拒绝自动切换
  • 需要人工确认后才能强制切换

建议

业务类型 推荐值 说明
普通业务 1MB ~ 10MB 接受少量数据丢失,优先保证可用性
金融交易 0 启用同步复制,零数据丢失
日志/分析 100MB+ 数据可重放,优先保证可用性

配置模板

Pigsty 提供了预置的配置模板,对应不同的权衡策略:

oltp.yml(默认)

优化在线事务处理,可用性优先

pg_conf: oltp.yml
pg_rto: 30
pg_rpo: 1048576  # 1MB
  • 异步复制,性能最优
  • 故障时可能丢失 < 1MB 数据
  • 适合大多数 Web 应用、业务系统

crit.yml

关键业务模板,一致性优先

pg_conf: crit.yml
pg_rto: 30
pg_rpo: 0
  • 同步复制,确保零数据丢失
  • 写入延迟增加(等待从库确认)
  • 从库故障会影响主库写入性能
  • 适合金融、支付等关键业务

olap.yml

优化分析查询,吞吐量优先

pg_conf: olap.yml
pg_rto: 60
pg_rpo: 10485760  # 10MB
  • 异步复制,大内存缓冲
  • 故障检测时间更长
  • 适合数据仓库、报表系统

权衡决策树

是否可接受任何数据丢失?
    │
    ├── 否 → 使用 crit.yml(同步复制,RPO=0)
    │         ├── 代价:写入延迟增加,从库故障影响主库
    │         └── 适用:金融、支付、核心交易
    │
    └── 是 → 可接受多少数据丢失?
              │
              ├── < 1MB(几秒数据)→ 使用 oltp.yml(默认)
              │         ├── 代价:极端故障丢失 < 1MB
              │         └── 适用:大多数在线业务
              │
              └── > 1MB → 根据需求调整 pg_rpo
                        ├── 代价:更多数据丢失风险
                        └── 适用:日志、分析、可重放数据

常见场景

场景一:电商系统

需求:高可用,可接受极少量订单丢失

pg_conf: oltp.yml
pg_rto: 30
pg_rpo: 1048576

理由:订单丢失可通过客服/重试补救,优先保证系统可用

场景二:银行核心系统

需求:零数据丢失,可接受略高延迟

pg_conf: crit.yml
pg_rto: 30
pg_rpo: 0

理由:资金交易不可丢失,宁可牺牲性能

场景三:日志分析平台

需求:高吞吐,数据可重新采集

pg_conf: olap.yml
pg_rto: 60
pg_rpo: 104857600  # 100MB

理由:日志可重新收集,优先保证写入性能


注意事项

  1. RTO 并非越低越好

    • 过低的 RTO 会导致网络抖动时频繁触发误切换
    • 每次切换都有业务闪断,频繁切换比一次长时间故障更糟糕
  2. RPO = 0 不是免费的

    • 同步复制会增加写入延迟(需等待从库确认)
    • 当所有同步从库不可用时,主库无法接受写入
  3. HA 不能替代备份

    • 高可用应对硬件故障,备份应对逻辑错误
    • DROP TABLE 会立即复制到所有从库
    • 需配合 PITR 应对人为误操作
  4. 定期验证

    • 通过 switchover 定期验证故障切换流程
    • 确保备份可恢复,而不只是"看起来在备份"