广告位 · 页头(后台-模块管理-新建模块后替换此区域)

PostgreSQL 流复制连接上限如何设置?max_wal_senders 与 max_replication_slots 参数详解

在PostgreSQL主从架构里,从库通过流复制连接主库拉取WAL日志,这条链路看似简单,背后却由两个参数共同把守着连接数量的上限:max_wal_senders和max_replication_slots。不少运维人员在新增从库或者做逻辑订阅时,突然发现连接建立失败,翻日志才意识到这两个参数的默认值根本不够用。这篇文章把两个参数的作用机制、取值计算和修改方法一次讲清楚。

PostgreSQL 流复制连接上限如何设置?max_wal_senders 与 max_replication_slots 参数详解

max_wal_senders 和 max_replication_slots 分别管什么

max_wal_senders控制的是主库上wal sender进程的数量上限。每一条物理流复制连接、每一次pg_basebackup基础备份、每一个逻辑解码的订阅端,都会在主库上占用一个wal sender进程。也就是说,它统计的不只是从库,而是所有需要主库持续推送WAL或逻辑数据的连接总和。默认值是10,表面上够用,但一旦你有多个从库、再加上备份任务和逻辑订阅,这个数字很快就会见底。

max_replication_slots则限制复制槽的最大数量。复制槽的作用是让主库记住每一个下游的消费进度,从而避免在从库还没收到WAL时就把日志回收掉。物理复制槽和逻辑复制槽共用这个上限,默认值同样是10。复制槽和wal sender是两个维度的资源:一个连接可以不使用复制槽,也可以绑定一个复制槽,所以两个参数需要分别评估,不能只调其中一个。

可以通过下面几条命令查看当前配置和实际占用情况:

-- 查看两个参数的当前值
SHOW max_wal_senders;
SHOW max_replication_slots;
-- 查看当前活跃的流复制连接,每行对应一个wal sender进程
SELECT pid, application_name, client_addr, state, sync_state
FROM pg_stat_replication;
-- 查看已经创建的复制槽
SELECT slot_name, slot_type, active, wal_status
FROM pg_replication_slots;

pg_stat_replication里的每一行对应一个wal sender进程,pg_replication_slots里的每一行对应一个复制槽。日常巡检时把这两个视图的行数和参数上限做对比,就能提前发现容量隐患,而不是等到连接报错才被动处理。

默认值为什么容易踩坑

默认值10在小型环境里问题不大,但实际生产中占用wal sender的连接远比想象中多。举个例子:3个从库占用3个,每晚一次pg_basebackup占用1个,如果还配了两个逻辑订阅给下游系统做数据同步,又占用2个,加起来已经6个。某天临时加一个从库、同时跑一个基础备份,连接数就可能突破10,此时新连接会被直接拒绝,日志里出现类似number of requested standby connections exceeds max_wal_senders的报错。

复制槽这边的问题更隐蔽。使用pg_basebackup的-C选项配合-S参数创建复制槽做基础备份时,如果脚本异常退出没有清理,槽会残留下来。残留的槽处于非active状态,但主库会一直为它保留WAL不敢删除,时间一长pg_wal目录持续膨胀,磁盘被写满,整个实例直接崩溃。所以max_replication_slots不仅是连接上限,更是一道需要配合监控的防线:调大它之前,先把僵尸槽的清理机制建好,否则调得越大,磁盘风险越高。

另一个容易被忽略的点是逻辑复制。逻辑订阅在发布端同样要占用复制槽和wal sender,而且订阅端故障时间过长时,逻辑槽保留的WAL会持续增长。评估参数取值时,务必把物理从库、备份任务、逻辑订阅三类消费者全部纳入统计,宁可留出余量,也不要贴着上限配置。

如何计算合理的取值

计算max_wal_senders的思路是把所有可能的消费者列出来再留出安全余量。假设你有4个物理从库、1个定时pg_basebackup任务、3个逻辑订阅,那么常态占用是8,考虑到临时排查问题、紧急重建从库时会同时开多个连接,建议在此基础上翻倍,配置成16或20。经验公式可以概括为:物理从库数加备份并发数加逻辑订阅数,再乘以2,结果向上取整。

max_replication_slots的取值逻辑类似,但要额外考虑临时槽的因素。pg_basebackup用-S指定固定槽,或者用-C临时创建槽位,都会消耗槽的配额。如果团队习惯并行跑多个备份任务,槽的数量至少要覆盖所有并行任务。一般来说,把max_replication_slots配置得和max_wal_senders相等或略大比较稳妥,因为不是每个连接都长期占槽,但槽一旦创建就可能长期存在。

下面是一个中型环境的参考配置,写在postgresql.conf中:

# 流复制相关参数
max_wal_senders = 20
max_replication_slots = 20
wal_level = replica

注意wal_level必须至少是replica才能支持流复制,如果要用逻辑复制则要改成logical。这两个参数都是postmaster级别的参数,修改后必须重启实例才能生效,这也是它们和max_connections这类可以直接reload生效的参数最大的区别,调整前要安排好停机窗口。

修改参数的正确操作步骤

第一步先确认当前占用情况,避免改完之后依然不够。第二步修改配置,可以直接编辑postgresql.conf文件,也可以用ALTER SYSTEM命令,后者会把配置写到postgresql.auto.conf中,优先级更高:

-- 用ALTER SYSTEM修改,无需手动编辑配置文件
ALTER SYSTEM SET max_wal_senders = 20;
ALTER SYSTEM SET max_replication_slots = 20;
-- 查看参数类别,确认生效方式
SELECT name, context FROM pg_settings
WHERE name IN ('max_wal_senders', 'max_replication_slots');

查询结果中context字段为postmaster,说明必须重启才能生效。第三步重启实例,Linux下用systemctl restart postgresql或者pg_ctl restart,Windows下如果是服务方式运行,用net stop和net start,或者直接在服务管理器里操作,数据目录通常类似D:\PostgreSQL\data这样的路径。第四步重启完成后,再次执行SHOW max_wal_senders确认新值生效,并观察pg_stat_replication确认所有从库都重新连上了。

重启前一定要确认所有从库具备自动重连机制,或者在业务低峰期执行。主库重启期间从库会进入断连等待状态,恢复后自动重连补齐期间的数据,这个过程不需要人工干预,但同步复制的场景下主库写入会被阻塞,需要提前评估业务影响。

常见报错与排查思路

遇到连接被拒绝时,先看主库日志。如果报错信息是requested WAL segment已被移除,说明从库落后太多,主库已经把需要的WAL回收了,这通常要配合复制槽或者调大wal_keep_size来解决,而不是调连接数。如果报错是无法创建复制槽、数量达到max_replication_slots上限,就要检查pg_replication_slots里有没有僵尸槽,用pg_drop_replication_slot手动清理:

-- 删除不再使用的复制槽
SELECT pg_drop_replication_slot('stale_slot_name');
-- 查看槽保留的WAL量,判断是否需要清理
SELECT slot_name, active,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
FROM pg_replication_slots;

如果报错是wal sender数量超限,先数一数pg_stat_replication里的连接,确认是哪些进程占用了配额,把不需要的备份连接或废弃的订阅停掉,再按前面说的步骤调大参数。排查的核心原则是:先看清当前占用,再决定是清理还是扩容,盲目调参数只会掩盖真实的问题。

总结一下,max_wal_senders和max_replication_slots是PostgreSQL流复制架构里的两道闸门,取值要把物理从库、备份任务、逻辑订阅全部算进去并留出余量;修改需要重启实例,要提前安排好窗口;同时务必配合复制槽监控,防止僵尸槽把磁盘撑爆。把这些细节提前规划好,从库扩容时才不会手忙脚乱。

广告位 · 文章底部(后台-模块管理替换此区域)