别把并发用错地方:理解 PostgreSQL 的 max_sync_workers_per_subscription

2026-09-28 38 预计阅读时间: 1 分钟
来源: postgr.es AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:7 分钟

在 PostgreSQL 逻辑复制的初始化阶段,max_sync_workers_per_subscription 控制一条订阅最多可以并发运行多少个表同步工作进程。最容易产生的误解是:把它调大,就能让某张超大表复制得更快。

实际并不是这样。这个参数并行的是不同表之间的初始复制,不会把同一张表的复制任务拆成多个并行分片。

它加速的是“多张表同时复制”

假设一个 publication 中有 40 张待初始化的表。较高的 max_sync_workers_per_subscription 可以让多张表同时进入同步流程,而不是逐张排队,因此可能显著缩短整个订阅的初始化时间。

但如果 publication 中只有一张 2 TB 的大表,即使把参数从 2 调到 8,也不会出现 8 个 worker 分段扫描并复制这张表。对这个场景而言,瓶颈仍然可能是:

  • 单表读取速度;
  • publisher 与 subscriber 的磁盘吞吐;
  • 网络带宽;
  • subscriber 写入及索引维护成本;
  • 初始化期间产生和保留 WAL 的压力。

可以把它理解成收银台:增加收银员能同时服务更多队伍,但不会让八名收银员共同扫描同一位顾客的购物车。

并发上限不只由一个 GUC 决定

max_sync_workers_per_subscription 不是孤立的资源开关。表同步 worker 还会占用逻辑复制 worker 和后台 worker 容量,因此调优时至少要一起检查:

  • max_sync_workers_per_subscription:单个订阅允许的表同步并发上限;
  • max_logical_replication_workers:实例可供逻辑复制使用的 worker 总容量;
  • max_worker_processes:后台 worker 的整体容量。

其中还要给订阅的常规 apply worker 以及其他逻辑复制任务留出空间。如果只调高第一个参数,而实例没有可用 worker,预期中的并发不会出现。

不同 PostgreSQL 版本对参数上下文和修改方式可能存在差异,因此不要凭经验判断是否需要重启。直接查看 pg_settings.context 和 pending_restart 更可靠。

可以这样检查和调整

下面的脚本在 subscriber 上执行。运行账户需要具有查看设置的权限;执行 ALTER SYSTEM 通常需要超级用户或相应权限。示例把并发值设为 8,生产环境应根据表数量、I/O 和 worker 预算修改。

psql -X -v ON_ERROR_STOP=1 <<'SQL'
SELECT name,
       setting,
       unit,
       context,
       source,
       pending_restart
FROM pg_settings
WHERE name IN (
    'max_sync_workers_per_subscription',
    'max_logical_replication_workers',
    'max_worker_processes'
)
ORDER BY name;

ALTER SYSTEM SET max_sync_workers_per_subscription = '8';
SELECT pg_reload_conf();

SELECT name,
       setting,
       context,
       source,
       pending_restart
FROM pg_settings
WHERE name = 'max_sync_workers_per_subscription';
SQL

如果查询结果显示 pending_restart = true,应按当前版本和运维流程安排重启,而不是反复执行 reload。若还需要修改 max_logical_replication_workers 或 max_worker_processes,同样先检查它们的 context 与重启要求。

初始化订阅后,可以在 subscriber 上观察订阅 worker。以下字段在常见版本中可用;如果当前版本的视图结构不同,先执行 \d+ pg_stat_subscription:

SELECT subname,
       pid,
       relid::regclass AS syncing_table,
       received_lsn,
       latest_end_lsn
FROM pg_stat_subscription
ORDER BY subname, relid NULLS FIRST;

relid 非空的行通常可以帮助识别正在同步的具体表。不要只看配置值,还要同时观察 CPU、磁盘延迟、网络吞吐以及 publisher 上的连接和 WAL 压力。

三种工作负载,三种判断方式

表很多,而且大小相近

这是最符合该参数设计目标的场景。适度提高并发,通常可以减少等待队列,让多个表同时完成初始复制。

建议逐级测试,例如从 2 调到 4,再调到 8,并记录完整初始化耗时和资源峰值。不要直接把值提高到表的总数。

一张表占据绝大多数数据量

提高该参数只能让其他小表与大表同时同步,无法并行拆分最大表。总完成时间仍可能由这张大表决定。

如果必须缩短停机或迁移窗口,可以考虑单独设计数据预装载、按业务边界拆表或使用支持并行数据迁移的工具。但这些方案涉及一致性快照、增量变更衔接和校验,不能仅靠设置 copy_data = false 就假定两端数据一致。

表很多,但 subscriber 已经 I/O 饱和

增加 worker 可能让性能变差:更多 COPY、索引写入和 WAL 处理会争抢同一批磁盘与 CPU。此时较低的并发反而能提供更稳定的吞吐和更小的业务抖动。

上线前的调优清单

调整前可以按下面的顺序判断:

  1. 统计待同步表的数量和大小分布,确认问题是“表很多”还是“单表很大”。
  2. 检查三个 worker 相关参数,并为 apply worker 和其他订阅预留容量。
  3. 在接近生产数据量的环境中测试完整初始化,而不是只复制几张空表。
  4. 同时监控 publisher 与 subscriber 的磁盘、网络、连接和 WAL 状态。
  5. 逐步提高并发;一旦资源利用率继续上升而总耗时不再下降,就停止加码。
  6. 如果最大表决定了迁移时间,转向单表迁移设计,而不是继续提高这个 GUC。

max_sync_workers_per_subscription 是一个有明确边界的并发旋钮:它擅长让多张表并排前进,却不会把一张大表切成多条快车道。先识别工作负载形态,再决定是否调高,通常比单纯追求更大的数字更有效。


相关推荐