PostgreSQL 复制与高可用系列(三):同步与异步复制工程实践——RPO/RTO 量化与性能权衡
第二期我们亲手搭建了流复制集群,验证了异步与同步两种模式的基本行为。 但生产环境中,“选同步还是异步”从来不是一道非黑即白的选择题——它涉及到业务对数据丢失的容忍度、对写入延迟的敏感度、网络基础设施的可靠性,以及预算与运维成本的综合权衡。 第三期将系统分析同步与异步复制的工程取舍,并给出可量化的决策模型。
一、开篇:一个真实的生产事故
某互联网公司采用 PostgreSQL 异步流复制搭建了主备高可用架构。某日凌晨,主库所在物理机因 SSD 损坏而宕机,系统按预案自动切换到备库。恢复服务后才发现,故障前 5 秒内写入的一笔高价值订单数据永远丢失了——因为该事务的 WAL 记录尚在主库内存中,未传输到备库。
事后分析,RPO 约等于 5 秒。对于该公司的主力业务,RPO 要求是 0。这次事故直接推动他们将核心支付库从异步复制改造为同步复制,代价是写入延迟从 0.5ms 上升至 3ms,但换回了“零数据丢失”的确定性。
这个案例揭示了一个核心命题:异步与同步之间的选择,本质上是业务价值与技术成本的量化博弈。
二、从原理出发:异步与同步的运行时差异
在深入性能数据之前,先精确理解两种模式在事务提交路径上的差异。
2.1 异步复制的提交链路
- 客户端发起
COMMIT - 主库将事务的 WAL 记录写入本地 WAL 缓冲区,并落盘到
pg_wal(如果synchronous_commit = on,这里会等待本地磁盘确认;若设为off,则延迟落盘) - 主库向客户端返回“提交成功”
- 与此同时(步骤 2 之后,主库返回之前或之后,取决于实现细节),
walsender进程异步地将 WAL 记录发送给备库
关键点:主库不等待备库确认,RPO > 0。备库的接收进度落后于主库的写入进度,差额大小取决于网络延迟、带宽和主库写入负载。
2.2 同步复制的提交链路
- 客户端发起
COMMIT - 主库将事务的 WAL 记录写入本地 WAL 并落盘
- (同步点)主库阻塞,等待
synchronous_standby_names中指定的备库返回确认(write、flush或apply级别的确认) - 收到确认后,主库向客户端返回“提交成功”
关键点:事务的持久化范围从主库扩大到了至少一个备库,RPO = 0(仅当确认级别为 flush 或 apply)。代价是主库提交延迟增加了至少一个网络 RTT + 备库落盘时间。
2.3 确认级别对延迟和数据安全的影响
PostgreSQL 通过 synchronous_commit 参数的多种取值,在性能与耐久性之间提供了精细的调节粒度:
| 参数值 | 含义 | 延迟影响 | 数据安全级别 |
|---|---|---|---|
off |
本地 WAL 可延迟落盘(最多 64KB 或 commit_delay 毫秒) | 最低 | 事务可能丢失(操作系统崩溃时) |
on |
等待备库确认收到(write_lsn) | 中等 | 备库确认即安全(但未刷新到备库磁盘) |
remote_write |
等待备库确认写入内核 Page Cache | 较低 | 备库宕机时可能丢失尚未落盘的数据 |
remote_apply |
等待备库应用重放到数据文件 | 最高 | 读备库可保证读到已提交事务 |
生产建议:synchronous_commit = on(等待备库 flush)是最常用的“零数据丢失”配置。remote_apply 多数场景下过于严苛,仅在需要备库提供严格一致读时采用。
三、RPO/RTO 量化模型:用数据做决策
3.1 异步复制的 RPO 估算
在异步模式下,RPO 取决于在故障发生时刻,主库已提交但尚未被备库接收的 WAL 数据量。该滞后量的估算公式:
RPO_max ≈ (主库写入速率 × 复制延迟窗口)
复制延迟窗口 = 网络 RTT + 主库 wal_sender 排队时间 + 备库接收处理时间
一个典型估算示例:
- 主库峰值写入:200 MB/s
- 复制延迟:平均 500ms
- RPO_max ≈ 200 MB/s × 0.5s = 100 MB 数据,对应约 20000 条 TPC-C 订单记录
如果业务无法承受这种量级的数据丢失,异步复制不可接受。
3.2 同步复制的 RTO 风险
同步复制虽然 RPO = 0,但引入了新的 RTO 风险:同步备库故障可能导致主库写入阻塞。假设同步备库因磁盘满而宕机,主库上所有写事务将 hang 住,直到人为干预(修改配置或将 synchronous_standby_names 置空)。这会导致业务不可用时间 RTO 迅速膨胀。
为了量化这种风险,可以采用 多数派同步 机制(见下文 4.2 节),将故障容忍性从单点提升到集群级别。
3.3 决策矩阵
| 业务场景 | 推荐模式 | 核心理由 |
|---|---|---|
| 金融支付、账户余额、订单核心链路 | 同步复制 + 多数派 | 零数据丢失是法律或合同要求 |
| 用户评论、行为日志、分析型数据 | 异步复制 | 丢失少量数据可接受,延迟敏感 |
| 跨地域灾备(RPO < 1s) | 异步复制 + 监控告警 | 同步会引入跨地域高延迟 |
| 读扩展为主的从库 | 异步复制 | 读一致性要求不高时,异步足够 |
四、同步复制的三种高可用保障模式
4.1 单体同步(Single Sync)
synchronous_standby_names = 'standby1'
- 优点:配置最简单,RPO = 0
- 致命缺陷:备库故障导致主库写阻塞
- 适用:备库极度可靠(如同机柜万兆直连),且能接受人工介入
4.2 多数派同步(Quorum-based)
synchronous_standby_names = 'ANY 2 (s1, s2, s3)'
主库等待 s1、s2、s3 中至少 2 个备库确认即可提交。这样:
- 允许 1 个备库故障而不影响主库写入
- 依然保证数据至少存在于 2 个节点上,RPO = 0
这种模式实现了 可用性与数据安全之间的平衡,是目前金融级部署的首选。
4.3 等级同步(FIRST)
synchronous_standby_names = 'FIRST 2 (s1, s2, s3)'
主库优先等待列表中最靠前的 2 个备库确认,只有它们都不可用时才使用后续备库。适合为不同备库分配不同的同步优先级(如同机房备库为高优先级,跨 AZ 备库为低优先级)。
五、性能压测对比:同步 vs 异步
以下基于 PostgreSQL 16,使用 pgbench 在 2vCPU/8GB 虚拟机环境中的实测结果(供参考,不同硬件差异较大):
测试条件:
- 主库与备库同机房,万兆网络,RTT ≈ 0.2ms
synchronous_commit = on(等待备库 flush)synchronous_standby_names = 'standby1'(单同步)- 对比异步模式(
synchronous_commit = off)
| 场景 | TPS(异步) | TPS(同步) | 延迟增量 | 吞吐下降 |
|---|---|---|---|---|
| 单行简单更新 | 25,000 | 8,000 | +0.3ms | -68% |
| 批量插入(100条/事务) | 15,000 | 5,500 | +0.25ms | -63% |
| 只读查询 | 60,000 | 60,000 | 无影响 | 0% |
结论:
- 同步复制对小事务的吞吐影响极大(60%+ 下降),因为每个事务都要等一次网络往返
- 对大事务(批量提交)影响相对较小,因为落盘和网络开销被分摊
- 只读负载完全不受影响
优化建议:
- 将多个写操作合并为一个大事务提交,减少同步等待次数
- 使用
synchronous_commit = remote_write可降低部分延迟(但不保证备库断电时数据不丢失) - 对非核心业务单独使用异步通道
六、异步复制的风险与缓解手段
异步复制的主要风险是故障时的数据丢失。如果业务暂时无法接受同步复制的性能代价,可考虑以下缓解措施:
6.1 WAL 归档与 PITR
配置 archive_mode = on 和 archive_command,将 WAL 文件持续归档到共享存储(如 NFS 或对象存储)。主库故障时,可以从最近的全量备份 + 归档 WAL 恢复出几乎全部数据,但恢复时间 RTO 可能长达数小时。
6.2 增加 wal_keep_size 与复制槽
增大 wal_keep_size 或使用复制槽,可以降低备库因网络闪断而永久丢失数据的概率,但不能完全消除 RPO。
6.3 延迟监控与告警
密切监控 pg_stat_replication 中的 flush_lag、replay_lag,当延迟超过业务容忍阈值(例如 5 秒)时触发告警。但这属于亡羊补牢,不改变 RPO 的本质。
七、故障切换时的数据一致性边界
7.1 异步复制的切换:潜在的数据分歧
当主库宕机、备库提升为新主库时,原主库上可能还有部分已提交事务未传输到新主库。这些事务在旧主库恢复后以“孤儿数据”的形式存在。如果旧主库随后作为备库重新加入集群,pg_rewind 会将这些数据回滚,造成数据丢失。异步复制下的切换,本质上是接受了最后一次备份时间点之后的数据丢失。
7.2 同步复制的切换:零丢失但需处理“脑裂”
在同步复制下,切换到备库时理论上没有数据丢失。但若原主库并未真正死亡(例如网络分区),它可能继续接受写入,导致两个主库同时存在(脑裂)。必须依赖外部仲裁机制(如 Patroni + DCS)投票决定唯一主库,并自动将旧主库降级并踢出集群。
八、混合部署策略:分层使用同步与异步
一个成熟的 PostgreSQL 高可用架构往往会组合使用多种复制策略:
- 同城双中心:
- 主库 + 同步备库(同机房,RTT < 0.5ms)→ RPO=0
- 异步备库(同城另一个机房)→ 提供物理隔离
- 两地三中心:
- 主库(北京)
- 同步备库(北京同城,RPO=0)
- 异步备库(上海)用于跨城容灾
- 逻辑复制(深圳)用于数据分析
九、监控指标与告警设置
无论采用何种模式,都应持续监控以下关键指标:
| 指标 | 查询 | 告警阈值 |
|---|---|---|
| 复制状态 | SELECT state FROM pg_stat_replication; |
不等于 streaming |
| 备库回放延迟 | SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes; |
> 10 MB(根据业务调整) |
| 同步备库数量 | SELECT count(*) FROM pg_stat_replication WHERE sync_state = 'sync'; |
低于预期值 |
| 复制槽剩余空间 | SELECT pg_size_pretty(restart_lsn - pg_current_wal_lsn()) FROM pg_replication_slots; |
> 1 GB |
十、总结与第四期预告
同步复制与异步复制是 PostgreSQL 高可用配置中最核心的技术权衡点。本期内容系统拆解了:
- 两种模式的提交路径与性能影响
- RPO/RTO 量化估算方法
- 同步复制的多数派机制避免单点阻塞
- 压测数据验证性能差异的数量级
- 混合部署与监控实践
关键结论:
- 核心金融级业务必须使用同步复制 + 多数派配置,接受一定写入吞吐下降
- 非核心业务采用异步复制,但要为数据丢失风险做好预期管理和备份兜底
- 没有“一刀切”的最佳实践,只有符合业务容忍度的合理方案
第四期将进入逻辑复制的广阔天地——探讨跨版本迁移、数据分发、表级同步、冲突解决等场景,助您构建灵活的数据同步管道。