• Welcome to HiddenMerit - Clyde's Blog
  • Welcome to try the game Torn: Referral Link
  • If you are my relative, friend, or netizen, quickly press Ctrl+D to bookmark Clyde's Blog
  • This site has a like feature. If you read any article, please hit the like button so I know someone has visited
  • Email: hiddenmeritATgmail.com (replace AT with @)

PostgreSQL 复制与高可用系列(三):同步与异步复制工程实践——RPORTO 量化与性能权衡

DBA Clyde Jin 3周前 (04-24) 14次浏览 0个评论

PostgreSQL 复制与高可用系列(三):同步与异步复制工程实践——RPO/RTO 量化与性能权衡

第二期我们亲手搭建了流复制集群,验证了异步与同步两种模式的基本行为。 但生产环境中,“选同步还是异步”从来不是一道非黑即白的选择题——它涉及到业务对数据丢失的容忍度、对写入延迟的敏感度、网络基础设施的可靠性,以及预算与运维成本的综合权衡。 第三期将系统分析同步与异步复制的工程取舍,并给出可量化的决策模型。

一、开篇:一个真实的生产事故

某互联网公司采用 PostgreSQL 异步流复制搭建了主备高可用架构。某日凌晨,主库所在物理机因 SSD 损坏而宕机,系统按预案自动切换到备库。恢复服务后才发现,故障前 5 秒内写入的一笔高价值订单数据永远丢失了——因为该事务的 WAL 记录尚在主库内存中,未传输到备库。

事后分析,RPO 约等于 5 秒。对于该公司的主力业务,RPO 要求是 0。这次事故直接推动他们将核心支付库从异步复制改造为同步复制,代价是写入延迟从 0.5ms 上升至 3ms,但换回了“零数据丢失”的确定性。

这个案例揭示了一个核心命题:异步与同步之间的选择,本质上是业务价值与技术成本的量化博弈

二、从原理出发:异步与同步的运行时差异

在深入性能数据之前,先精确理解两种模式在事务提交路径上的差异。

2.1 异步复制的提交链路

  1. 客户端发起 COMMIT
  2. 主库将事务的 WAL 记录写入本地 WAL 缓冲区,并落盘到 pg_wal(如果 synchronous_commit = on,这里会等待本地磁盘确认;若设为 off,则延迟落盘)
  3. 主库向客户端返回“提交成功”
  4. 与此同时(步骤 2 之后,主库返回之前或之后,取决于实现细节),walsender 进程异步地将 WAL 记录发送给备库

关键点:主库不等待备库确认,RPO > 0。备库的接收进度落后于主库的写入进度,差额大小取决于网络延迟、带宽和主库写入负载。

2.2 同步复制的提交链路

  1. 客户端发起 COMMIT
  2. 主库将事务的 WAL 记录写入本地 WAL 并落盘
  3. (同步点)主库阻塞,等待 synchronous_standby_names 中指定的备库返回确认(writeflushapply 级别的确认)
  4. 收到确认后,主库向客户端返回“提交成功”

关键点:事务的持久化范围从主库扩大到了至少一个备库,RPO = 0(仅当确认级别为 flushapply)。代价是主库提交延迟增加了至少一个网络 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 = onarchive_command,将 WAL 文件持续归档到共享存储(如 NFS 或对象存储)。主库故障时,可以从最近的全量备份 + 归档 WAL 恢复出几乎全部数据,但恢复时间 RTO 可能长达数小时。

6.2 增加 wal_keep_size 与复制槽

增大 wal_keep_size 或使用复制槽,可以降低备库因网络闪断而永久丢失数据的概率,但不能完全消除 RPO。

6.3 延迟监控与告警

密切监控 pg_stat_replication 中的 flush_lagreplay_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 量化估算方法
  • 同步复制的多数派机制避免单点阻塞
  • 压测数据验证性能差异的数量级
  • 混合部署与监控实践

关键结论

  • 核心金融级业务必须使用同步复制 + 多数派配置,接受一定写入吞吐下降
  • 非核心业务采用异步复制,但要为数据丢失风险做好预期管理和备份兜底
  • 没有“一刀切”的最佳实践,只有符合业务容忍度的合理方案

第四期将进入逻辑复制的广阔天地——探讨跨版本迁移、数据分发、表级同步、冲突解决等场景,助您构建灵活的数据同步管道。


绩隐金 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:PostgreSQL 复制与高可用系列(三):同步与异步复制工程实践——RPORTO 量化与性能权衡
喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址