同步级别(remote_apply / remote_write / on)
remote_write、on、remote_apply 是 synchronous_commit 三个「真正等待备库」的档位。它们的名字看起来相近,实际含义却分别对应提交路径上的三个不同位置:备库的页缓存、备库的磁盘、备库的重放进度。本页把这三档放在一起深入对比。
理解这三档的关键是「越靠后越安全、越慢」这条主线。remote_write 只保证备库「收到了」,on 保证备库「存下来了」,remote_apply 保证备库「已经能用这份数据回答查询」。三者对 RPO 的贡献依次提高,对提交延迟的影响也依次增加。
本页会给出三档的时序对比、延迟量级、适用场景,以及如何在实际环境中测量提交延迟。这些数字和判定标准是决定「我的业务该用哪一档」的直接依据。
前置知识
基础概念
在对比三档之前,先明确几个贯穿全页的概念。它们的定义直接决定了三档之间的差异在哪。
| 概念 | 说明 |
|---|---|
| write(写入) | 把 WAL 交给操作系统,数据进入页缓存 |
| flush(落盘) | 调用 fsync,把页缓存中的数据真正写入持久化存储 |
| replay(重放) | 备库把 WAL 记录应用到数据文件,使变更可见 |
| 提交延迟 | 从客户端发出 COMMIT 到收到成功响应的耗时 |
| RPO | 允许丢失多少数据 |
| 可见性延迟 | 主库提交后,备库上多久能看到这条数据 |
三档速览
| 档位 | 备库要确认什么 | 备库宕机后果 | 备库能否立即读到 |
|---|---|---|---|
remote_write | WAL 已写入备库页缓存 | 可能丢最近若干事务 | 不能 |
on | WAL 已 fsync 到备库磁盘 | 不丢 | 不能 |
remote_apply | WAL 已在备库重放完成 | 不丢 | 能 |
一句话总结三者的差异:
remote_write:备库「手里有了」。on:备库「存稳了」。remote_apply:备库「能用了」。
为什么需要三档而不是一档
如果只看「不丢数据」这个目标,on 就够了。那为什么还需要 remote_write 和 remote_apply?因为它们分别优化了两个不同的目标:
remote_write优化延迟:省掉备库的一次 fsync。在备库存储较慢(例如机械盘或网络存储)时,这一步可能占提交延迟的相当比例。remote_apply优化读一致性:让备库在事务提交后立刻可见。这是读写分离架构「写后读」场景的刚需。
所以三档并不是「越来越好」的简单排序,而是覆盖了三个不同的工程需求点。选型时要问的是「我的瓶颈和约束是什么」,而不是「哪一档最安全」。
与 synchronous_standby_names 的关系
三档只有在 synchronous_standby_names 非空时才有意义。这一点在本页反复出现,因为「配置了 remote_apply 但没配同步备库」是最常见的踩坑方式:此时 remote_apply 会静默退化为「等本地落盘」,参数看起来生效了,实际效果与 on 在无备库时完全一样。
因此本页讨论的所有延迟数字与行为,前提都是「synchronous_standby_names 非空且至少有一个备库处于 sync 或 quorum 状态」。
语法与用法
三档通过 synchronous_commit 设置:
# postgresql.conf
synchronous_commit = remote_write
# 或
synchronous_commit = on
# 或
synchronous_commit = remote_apply会话级与事务级设置:
-- 会话级
SET synchronous_commit = remote_apply;
-- 事务级:本事务要求备库可读
BEGIN;
SET LOCAL synchronous_commit = remote_apply;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- 后续事务恢复会话默认
SET synchronous_commit = on;按业务分级设置(不同角色用不同档位):
-- 核心交易角色:要求备库立即可读
ALTER ROLE core_app SET synchronous_commit = remote_apply;
-- 报表写入角色:只要求不丢,不要求立即可读
ALTER ROLE report_app SET synchronous_commit = on;
-- 日志采集角色:允许丢一点
ALTER ROLE log_app SET synchronous_commit = off;查看当前设置与来源:
SELECT name, setting, source, sourcefile
FROM pg_settings
WHERE name = 'synchronous_commit'; name | setting | source | sourcefile
--------------------+--------------+-------------+--------------------------------------------
synchronous_commit | remote_apply | role | /var/lib/pgsql/18/data/postgresql.conf
(1 row)source 列会告诉你是数据库级、角色级还是会话级设置,这在排查「为什么我的档位和别人不一样」时非常有用。
详细说明
remote_write 深入
remote_write 要求备库确认「WAL 已经写进它的文件系统(页缓存)」。注意这里的措辞:写入文件系统不等于落盘。备库的 walreceiver 进程调用 write() 把数据交给内核,数据进入操作系统页缓存,此时备库就向主库发出确认。
它带来的保证是:
- 主库崩溃(断电、宕机):备库手上有这批 WAL,可以继续提供服务,数据不丢。
- 备库崩溃(OS 崩溃、掉电):页缓存中的 WAL 可能丢失,此时可能丢数据。
因此 remote_write 的准确表述是「防主库单点故障,但不防备库自身崩溃」。它的延迟优势在于省掉了备库的一次 fsync——在高延迟存储(网络块存储、机械阵列)上,这个节省可能是几毫秒。
一个容易被忽略的问题是:如果主备在同一机架、同一电源域,那么「主库掉电」与「备库掉电」往往是同时发生的。这种情况下 remote_write 提供的保护几乎为零,却仍然付出了网络往返的延迟代价。因此使用 remote_write 前,应当先问「主库和备库的故障域是否真的独立」。
remote_write 适合的场景特征:备库存储的 fsync 延迟明显高于网络 RTT,且主备故障域独立(例如跨机架、跨机房)。
on 深入
on 在三档中处于「标准配置」的位置。它要求备库把 WAL fsync 到持久化存储,因此无论主库还是备库崩溃,已提交事务都不会丢。代价是每次提交多一次备库磁盘 fsync 的等待。
on 的延迟构成可以拆解为:
提交延迟(on) ≈ 主库 WAL flush + 网络 RTT(半程) + 备库 WAL flush + 网络 RTT(半程)
≈ 本地 fsync + RTT + 备库 fsync用一个具体例子:本地 fsync 1ms、RTT 0.5ms、备库 fsync 2ms,则单次提交的总等待约 3.5ms。如果业务是 1000 TPS 的独立小事务,同步复制的理论上限就是 1000 / 0.0035 ≈ 285 并发才能撑住——这就是为什么高频小事务在同步复制下必须提高并发度或改用批量提交。
on 是默认值,也是绝大多数高可用部署的推荐档位。它的「性价比」在三档中最高:拿到了「主备都不丢」的完整保证,只比 remote_write 多一次备库 fsync。
一个重要的运维结论是:使用 on 时,备库的存储性能会直接影响主库的提交延迟。备库不能被当作「随便配一台就行」的冷备,尤其不能使用性能远低于主库的存储,否则会成为主库的性能瓶颈。
remote_apply 深入
remote_apply 是要求最高的一档。它不仅要求备库落盘,还要求备库把 WAL 重放(replay)完成,使该事务在备库上「对查询可见」。这样做的直接收益是:在备库上执行只读查询时,读到的一定是主库已经提交过的数据。
这个特性解决了读写分离架构中最难处理的一致性问题。在没有 remote_apply 之前,「写主库、立刻读备库」需要应用层做各种补偿:写后强制读主库、加人为延迟、用会话粘滞等。remote_apply 把这个问题下沉到数据库层面解决。
代价有两部分:
- 正常情况下的额外延迟:多等一次备库重放的时间,通常几百微秒到几毫秒。
- 异常情况下的放大:如果备库的重放被长查询阻塞(
max_standby_streaming_delay生效中)、或者备库 I/O 跟不上,重放延迟会显著上升,进而直接拖慢主库提交。
第二点风险常被低估。在 remote_apply 模式下,备库上的一个慢查询可能变成主库的性能事故:如果备库上的查询阻止了 WAL 重放(例如有长时间运行的事务快照),主库的提交就会一直等下去。因此使用 remote_apply 时,必须同时关注备库上的长事务与 max_standby_streaming_delay 设置。
remote_apply 是 PostgreSQL 9.6 引入的档位。
三档的时序对比
用文字时序图描述三档的差异,可以更直观地看到它们分别在哪里「停下等待」:
remote_write:
客户端 ──COMMIT──► 主库写入 ──► 主库 flush ──► 传输 ──► 备库 write
│
客户端 ◄────────── 成功响应 ◄────────────────────────────────┘
(不等备库 fsync,不等重放)
on:
客户端 ──COMMIT──► 主库写入 ──► 主库 flush ──► 传输 ──► 备库 write ──► 备库 fsync
│
客户端 ◄────────── 成功响应 ◄─────────────────────────────────────────────┘
(不等重放)
remote_apply:
客户端 ──COMMIT──► 主库写入 ──► 主库 flush ──► 传输 ──► 备库 write ──► 备库 fsync ──► 备库 replay
│
客户端 ◄────────── 成功响应 ◄──────────────────────────────────────────────────────────┘
(备库已可读)从图中可以清楚看到:三档的差异不是「等待的强度」,而是等待链的长度。remote_write 在备库 write 后返回,on 在备库 fsync 后返回,remote_apply 在备库 replay 后返回。
延迟量级对比
下面是三档在典型环境下的延迟增量参考(以异步复制为基线)。这些数字是量级参考,实际值高度依赖网络与存储。
| 档位 | 同机房(<1ms RTT) | 同城跨机房(1~5ms RTT) | 跨地域(20~50ms RTT) |
|---|---|---|---|
| 异步(基线) | ~0.1ms | ~0.1ms | ~0.1ms |
remote_write | +0.3~1ms | +1~6ms | +20~55ms |
on | +0.5~2ms | +2~10ms | +25~60ms |
remote_apply | +1~3ms | +3~15ms | +30~70ms |
从表中可以读出一个关键结论:跨地域部署时,三档之间的差异被网络 RTT 淹没。在 30ms RTT 的环境里,remote_write 与 remote_apply 的差距可能只有几毫秒,而两者相对异步复制的开销都是 30ms 级别。这意味着:跨地域部署时选择哪一档意义不大,真正的决策点是「要不要同步」;同机房部署时,档位选择才真正影响性能。
适用场景对照
| 业务特征 | 推荐档位 | 理由 |
|---|---|---|
| 金融核心交易,主备都不许丢,需读一致性 | remote_apply | 兼顾不丢与写后立即可读 |
| 一般 OLTP,主备都不许丢 | on | 标准配置,性价比最高 |
| 高频小事务、备库存储慢 | remote_write | 省掉备库 fsync,但需确认故障域独立 |
| 日志采集、埋点上报 | off | 允许丢最近若干事务 |
| 批处理后台任务 | local | 只要本地持久化 |
| 单机无备库 | on(默认) | 等价于本地落盘 |
选型时的一个实用原则是:先确定「备库能不能丢」,再确定「备库要不要立即可读」。第一个问题的答案区分 remote_write 与 on/remote_apply,第二个问题的答案区分 on 与 remote_apply。
与 hot_standby_feedback 的配合
hot_standby_feedback 让备库把「当前最老的活动查询快照」反馈给主库,主库据此避免回收备库仍需要的行版本(避免备库上出现「查询被取消」)。它与 remote_apply 有直接的交互关系:
hot_standby_feedback | 与 remote_apply 的关系 |
|---|---|
on | 备库的长查询会被主库感知,减少查询取消;但可能加剧主库表膨胀 |
off(默认) | 主库不知道备库上有长查询,备库上的查询可能被取消以推进重放 |
在 remote_apply 模式下,「备库能否及时重放」直接决定主库提交延迟。如果备库因为冲突处理而反复中断重放(取消查询),重放进度会受影响,进而拖慢主库。因此使用 remote_apply 时,通常建议:
- 评估备库上的查询是否可能长期阻塞重放;
- 根据业务偏好选择
hot_standby_feedback的开与关; - 明确设置
max_standby_streaming_delay,给重放与查询冲突一个明确的上界。
这三项是 remote_apply 场景下的「配套三件套」,缺一项都可能在上线后表现为「主库莫名其妙变慢」。
各档对 RPO 的影响
RPO(允许丢失多少数据)在三档下的表现各不相同:
| 档位 | 主库故障 RPO | 备库故障 RPO | 说明 |
|---|---|---|---|
remote_write | 0 | >0 且不可控 | 备库页缓存丢失量取决于崩溃时机 |
on | 0 | 0 | 主备任一崩溃都不丢已提交事务 |
remote_apply | 0 | 0 | 与 on 相同的持久性,额外获得读可见性 |
只有 on 与 remote_apply 能做到「主备双向 RPO = 0」。这是一条比延迟更重要的判定标准:如果业务要求任何一方崩溃都不丢数据,那么 remote_write 就不合格,必须用 on 或 remote_apply。
反过来说,如果业务只防备「主库整台机器没了」,而对备库自身崩溃的丢失能接受,那么 remote_write 是成立的。这个区别通常被表述为「单点故障防护」与「双向持久化防护」的差异。
跨机房延迟的考量
跨机房部署时,物理距离带来的 RTT 是不可绕过的下限。可以用下面的公式估算同步复制的吞吐上限:
单连接最大 TPS ≈ 1 / 提交延迟
并发所需连接数 ≈ 目标 TPS × 提交延迟以 RTT = 30ms、目标 5000 TPS 为例:
提交延迟 ≈ 30ms(跨机房 RTT)
所需并发 ≈ 5000 × 0.03 = 150 个并发连接也就是说,要维持 5000 TPS,需要至少 150 个并发连接在同步等待。这在连接数上是可以达到的,但如果业务本身并发度只有 20,那么实际 TPS 上限只有约 666。
这个估算说明了一个重要事实:同步复制限制的是「提交延迟 × 并发度」的乘积,而不是单纯的 TPS。在跨机房的同步复制场景下,提高并发度或改用批量提交是唯一的性能出路。
另一个可行的方案是「同机房同步 + 跨机房异步」的两级架构:把同机房的备库设为同步备库(保证 RPO = 0),把跨机房的备库设为异步备库(用于地域级容灾)。这样提交延迟只承担极低的同机房 RTT,而跨机房副本仍能在灾难时派上用场,代价是灾难切换时会丢一部分数据。
测量提交延迟的方法
选择档位之前,必须先测出自己环境下的实际延迟。下面三种方式由简到繁。
方法一:psql 的 \timing
\timing on
INSERT INTO t_probe(id, ts) VALUES (1, now());INSERT 0 1
Time: 3.412 ms优点是简单直接;缺点是包含了客户端与 SQL 解析开销,且单次测量噪声较大,应该重复多次取中位数。
方法二:pgbench 在同步复制下的 TPS
# 初始化
pgbench -i -s 50 pgbench
# 只读不做;这里用简单的 UPDATE 类事务观察写延迟
pgbench -c 32 -j 8 -T 60 -f update.sql pgbenchupdate.sql 内容示例:
UPDATE pgbench_accounts SET abalance = abalance + 1 WHERE aid = :aid;对比开启/关闭同步复制时的 TPS 与 latency average,可以直接量化同步复制的代价。pgbench 输出的 latency average 就是平均提交延迟,是选型时最需要的数字。
方法三:在 pg_stat_replication 侧交叉验证
SELECT application_name,
pg_wal_lsn_diff(pg_current_wal_lsn(), flush_lsn) AS flush_lag_bytes,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_lag_bytes,
write_lag, flush_lag, replay_lag
FROM pg_stat_replication; application_name | flush_lag_bytes | replay_lag_bytes | write_lag | flush_lag | replay_lag
------------------+-----------------+------------------+--------------+--------------+--------------
standby1 | 0 | 409600 | 00:00:00.000 | 00:00:00.001 | 00:00:00.083
(1 row)flush_lag 与 replay_lag 是主备之间在「收到时间」上的差异度量,可以直接用来判断瓶颈在传输还是在重放。如果 flush_lag 很小但 replay_lag 明显较大,说明瓶颈在备库重放——这正是 remote_apply 会比 on 慢很多的直接原因。
remote_apply 的代价拆解
把 remote_apply 相对 on 的额外代价拆开看,可以更准确地判断是否值得用:
| 代价来源 | 表现 | 缓解手段 |
|---|---|---|
| 重放本身的耗时 | 每事务几百微秒~几毫秒 | 提升备库 I/O、减少备库负载 |
| 查询与重放冲突 | 长查询阻塞重放,延迟突增 | 调整 max_standby_streaming_delay、hot_standby_feedback |
| 批量提交放大 | 一次提交要等一批 WAL 重放 | 减少单事务写入量,避免超大事务 |
| 备库负载波动 | 备库上跑报表时主库变慢 | 用独立的异步备库承接报表 |
从表中可以看出,「备库上的查询负载」与「主库的提交延迟」在 remote_apply 下被耦合了起来。这是最需要在架构评审时解释清楚的一点:如果备库同时承担报表查询,那么报表的负载会直接反映到线上写入延迟上。
一个常见的架构解法是:建立两台备库,一台同步(不接查询或只接轻查询),一台异步(承接报表)。同步备库保证 RPO 与写后可读,异步备库保证查询不影响主库。
业务选型建议
把前面的分析归纳为可执行的选型建议:
| 业务类型 | 档位建议 | 关键考量 |
|---|---|---|
| 银行核心账务 | remote_apply | 不丢 + 写后立即可读,延迟可接受 |
| 电商订单 | on | 不丢优先,读一致性用其他手段解决 |
| 支付网关 | on 或 remote_apply | 取决于是否有跨库读一致性需求 |
| 社交内容 | on 或 remote_write | 视备库存储与故障域而定 |
| 日志/监控 | off | 允许丢最近数据 |
| 数据仓库写入 | local | 只要求本地持久化 |
从这张表可以概括出一条主线:互联网业务多用 on,金融业务多用 remote_apply,可容忍丢失的业务用 off。中间的 remote_write 是一个较少使用但有其位置的折中选项,主要用于「备库存储慢但主备故障域独立」的特殊场景。
实战示例
示例一:测量三档下的实际提交延迟
在同一实例、同一备库上依次切换档位并测量:
-- 基线:remote_write
ALTER SYSTEM SET synchronous_commit = 'remote_write';
SELECT pg_reload_conf();pgbench -c 32 -j 8 -T 30 -f update.sql pgbenchlatency average = 1.842 ms
tps = 17372.66 (without initial connection time)-- 切到 on
ALTER SYSTEM SET synchronous_commit = 'on';
SELECT pg_reload_conf();pgbench -c 32 -j 8 -T 30 -f update.sql pgbenchlatency average = 3.115 ms
tps = 10274.31 (without initial connection time)-- 切到 remote_apply
ALTER SYSTEM SET synchronous_commit = 'remote_apply';
SELECT pg_reload_conf();pgbench -c 32 -j 8 -T 30 -f update.sql pgbenchlatency average = 4.907 ms
tps = 6521.09 (without initial connection time)三次结果可以直接对比出档位的代价:本环境中 on 相对 remote_write 增加约 1.3ms(备库 fsync 的代价),remote_apply 相对 on 再增加约 1.8ms(备库重放的代价)。这样的数据是选型最有力的依据。
示例二:观察 remote_apply 下的重放成为瓶颈
SET synchronous_commit = 'remote_apply';
-- 在备库上制造一个长时间运行的查询(模拟报表)
-- 备库会话中执行:
SELECT pg_sleep(120), count(*) FROM large_table;
-- 回到主库,观察重放延迟
SELECT application_name, write_lag, flush_lag, replay_lag
FROM pg_stat_replication; application_name | write_lag | flush_lag | replay_lag
------------------+--------------+--------------+--------------
standby1 | 00:00:00.001 | 00:00:00.002 | 00:00:47.318
(1 row)write_lag 与 flush_lag 都在毫秒级,但 replay_lag 达到 47 秒。这说明备库的 WAL 接收与落盘都正常,瓶颈完全在重放——完全符合「备库长查询阻塞重放」的判断。此时在主库上测量提交延迟,会看到与 replay_lag 同量级的暴涨。
这个示例很好地说明了 remote_apply 的风险:备库侧的一个慢查询可以直接拖慢主库的提交。
示例三:按业务角色分级配置
-- 核心交易角色:要求写后立即可读
ALTER ROLE core_app SET synchronous_commit = remote_apply;
-- 一般业务角色:只要求不丢
ALTER ROLE normal_app SET synchronous_commit = on;
-- 批量导入角色:允许丢一点,换取速度
ALTER ROLE bulk_app SET synchronous_commit = off;验证不同角色的设置:
-- 以 core_app 身份连接后
SELECT current_setting('synchronous_commit') AS core_setting; core_setting
--------------
remote_apply
(1 row)用角色级设置的好处是:业务方不需要在自己的代码里写 SET,数据库层面就完成了分级,且新连接自动继承。这比让每个应用自己在连接初始化时 SET 更不容易出错。
示例四:为批量任务临时降档
\timing on
-- 常规写法:逐条提交,每次都付同步延迟
INSERT INTO t_order(id, amount) VALUES (1, 100);
INSERT INTO t_order(id, amount) VALUES (2, 200);
-- ... 重复 10000 次INSERT 0 1
Time: 4.882 ms-- 优化写法:一个事务批量写入 + 事务级降档
BEGIN;
SET LOCAL synchronous_commit = off;
INSERT INTO t_order(id, amount)
SELECT g, g * 100 FROM generate_series(1, 10000) g;
COMMIT;INSERT 0 10000
Time: 63.104 ms一万行的批量导入从「按单行延迟估算的约 48 秒」降到 63ms。这说明在同步复制环境下,批量化与降档是两个可以叠加的优化手段:批量化减少提交次数,降档缩短单次提交的等待。
示例五:验证 remote_apply 的读可见性
在主库上写入,然后立刻在备库上查询:
-- 主库会话:使用 remote_apply
SET synchronous_commit = 'remote_apply';
INSERT INTO t_visibility(id, note) VALUES (1, 'written by primary');INSERT 0 1-- 备库会话:立刻查询(不需要任何等待)
SELECT id, note FROM t_visibility WHERE id = 1; id | note
----+---------------------
1 | written by primary
(1 row)在 remote_apply 下,主库返回 INSERT 0 1 时,这条数据已经在备库上可见。如果换成 on,同样的查询可能出现「查不到」的结果——这正是 remote_apply 与 on 在读写分离场景下的实际差别。
示例六:确认档位是否真的生效
-- 三个检查点
SELECT
current_setting('synchronous_commit') AS commit_level,
current_setting('synchronous_standby_names') AS standby_names,
(SELECT count(*) FROM pg_stat_replication
WHERE sync_state IN ('sync', 'quorum')) AS active_sync_standbys; commit_level | standby_names | active_sync_standbys
--------------+-----------------------------+---------------------
remote_apply | FIRST 1 (standby1, standby2) | 1
(1 row)三项都符合预期,才能说明「remote_apply 正在生效」:档位是 remote_apply、同步备库列表非空、且至少有一个备库处于 sync 或 quorum。少任何一项,remote_apply 都是「名义生效、实际退化」。
注意事项
remote_write 不防备库自身崩溃。 备库的 OS 崩溃或掉电会丢失页缓存中的 WAL。如果主备处于同一故障域(同机架、同电源),它的保护价值会大幅降低。
on 会让备库的存储性能直接影响主库提交延迟。 这一点在容量规划时经常被忽略。备库不能使用明显慢于主库的存储,否则会拖慢主库。
remote_apply 会让备库的查询负载影响主库写入。 这是它最大的隐性风险。备库上的长查询可能阻塞重放,进而导致主库提交等待。使用前必须评估备库的查询模式。
三档都需要 synchronous_standby_names 非空才生效。 空值时三档全部退化为「等本地落盘」,参数看起来生效但实际无效。任何关于档位的结论都必须先确认这一点。
不要在高频小事务场景下直接上 remote_apply。 建议先测量基准延迟,再估算 目标 TPS × 提交延迟 所需的并发数,判断是否可达。
跨地域部署时档位差异会被 RTT 淹没。 真正的决策点是「要不要跨地域同步」,而不是选哪一档。跨地域同步通常建议采用「同机房同步 + 跨机房异步」的两级架构。
延迟备库不要与 remote_apply 组合。 如果同步列表中包含设置了 recovery_min_apply_delay 的备库,remote_apply 会让主库提交等待整个延迟时长。
remote_apply 是 PostgreSQL 9.6 引入的。 在更早版本上没有这一档,老版本上做选型时不要把它列为可选项。
备库的连接是否 streaming 决定了档位是否有效。 备库进程在但状态不是 streaming(例如正在追赶、或已 stopping)时,同步确认无法完成,会形成写阻塞。
档位切换会影响正在等待的提交。 把档位从 remote_apply 降到 on 会立刻唤醒等待重放的会话;反向操作会让后续提交开始变慢。变更时应有心理预期。
最佳实践
先测后选,不要凭直觉。 在真实环境用
pgbench测出三档的latency average,用数据决策,而不是用「感觉慢不慢」。默认使用
on。 它是三档中性价比最高的:拿到主备双向 RPO = 0 的完整保证,只比remote_write多一次备库 fsync。只有在「备库存储慢 + 故障域独立」时才考虑
remote_write。 这两个条件缺一个,remote_write的收益就不成立。只有在「需要写后立即可读」时才使用
remote_apply。 读一致性问题优先考虑在应用层解决(写后读主库),把remote_apply留给真正无法绕过的场景。用角色级
ALTER ROLE ... SET做业务分级。 让数据库层面完成档位分级,避免依赖每个应用自行SET,减少遗漏。为
remote_apply配置独立的同步备库。 同步备库不承接或只承接轻量查询,把报表类负载放到异步备库上,避免查询拖慢主库。使用
remote_apply时同时设定max_standby_streaming_delay与hot_standby_feedback。 这两项决定了「重放与查询冲突」的处理方式,是remote_apply稳定运行的必要配套。监控
replay_lag与flush_lag的差值。 这个差值是remote_apply额外延迟的直接反映,持续拉大说明备库重放跟不上,需要告警。批量任务一律批量化 + 降档。
SET LOCAL synchronous_commit = off配合多行INSERT ... SELECT,可以同时减少提交次数与单次等待。跨地域场景优先采用两级复制架构。 同机房同步保证 RPO = 0,跨机房异步用于地域容灾,把延迟成本控制在同机房 RTT 水平。
每次档位变更都做一次回归压测。 档位变更会改变系统的延迟特征,可能触发连接池耗尽、超时重试等连锁问题,不能仅凭「参数改了」就上线。
把档位与业务等级对应关系写进文档。 明确记录「哪些业务用哪一档、为什么」,避免后续有人为了「更安全」而全局提到
remote_apply,反而降低可用性。
下一节
继续学习:quorum 同步提交
参考链接
- PostgreSQL 官方文档:Write Ahead Log — Configuration Parameters
- PostgreSQL 官方文档:Replication — Configuration Parameters
- PostgreSQL 官方文档:Log-Shipping Standby Servers
- PostgreSQL 官方文档:Hot Standby
- PostgreSQL 官方文档:Monitoring Database Activity
- PostgreSQL 官方文档:pgbench
- PostgreSQL 官方文档:High Availability, Load Balancing, and Replication
- PostgreSQL 官方文档:ALTER ROLE
- PostgreSQL 官方文档:SET — Set Run-time Parameters
- PostgreSQL 官方文档:Server Configuration — Managing Configuration Files