复制槽(Replication Slot)是PostgreSQL 9.4版本引入的重要机制,它的核心作用是确保主库在备库或逻辑订阅端尚未接收WAL日志之前,不会过早删除这些日志。这个设计解决了流复制场景下备库断线重连后需要重新做基础备份的痛点,但同时也带来了新的运维风险:如果备库永久下线而复制槽没有被清理,主库会一直保留WAL日志,最终把磁盘写满导致数据库崩溃。因此,正确地创建、监控和删除复制槽,是每个PostgreSQL运维人员必须掌握的技能。

复制槽的工作原理与两种类型
要理解复制槽的管理逻辑,首先要明白它的工作机制。PostgreSQL的WAL日志是循环复用的,主库通过参数wal_keep_size(旧版本为wal_keep_segments)保留一定量的旧日志。但这个保留是盲目的,主库并不知道备库到底消费到了哪个位置。复制槽的出现让主库能够精确感知下游的消费进度:每个槽会记录一个restart_lsn位置,只有当所有活跃复制槽都确认消费了某段WAL之后,主库才允许回收这段日志。
wal_level = logical的基础上,配合逻辑解码插件(如pgoutput、wal2json)将WAL翻译成可读的逻辑变更事件,常用于数据同步到异构系统或跨版本升级。两者的创建语法略有差异,逻辑槽需要指定输出插件。可以用下面的查询查看当前实例上所有的复制槽状态:
SELECT slot_name, slot_type, active, restart_lsn, wal_status,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
FROM pg_replication_slots;
其中wal_status字段是PostgreSQL 13新增的,取值为reserved(正常)、extended(已延伸到wal_keep_size之外)、unreserved(可能丢失)和lost(已丢失,槽失效),是判断槽健康状态的关键指标。
创建复制槽的几种方式
最常用的方式是在psql中通过SQL函数创建。物理槽使用pg_create_physical_replication_slot,逻辑槽使用pg_create_logical_replication_slot:
-- 创建物理复制槽,一般用于流复制
SELECT * FROM pg_create_physical_replication_slot('standby1_slot');
-- 创建逻辑复制槽,指定pgoutput解码插件
SELECT * FROM pg_create_logical_replication_slot('sub_slot', 'pgoutput');
物理槽的典型使用场景是配合primary_slot_name参数。在备库的postgresql.conf中配置primary_conninfo后,再设置primary_slot_name = 'standby1_slot',备库重连时就会通过这个槽上报消费位点,主库据此决定WAL保留策略。
另一种方式是通过复制协议创建,常见于pg_basebackup和逻辑订阅场景。pg_basebackup加-C -S myslot参数会在做基础备份时自动创建物理槽;逻辑复制的发布订阅体系中,订阅端执行CREATE SUBSCRIPTION时若不加with (create_slot = false),也会自动在发布端创建逻辑槽。此外,pg_recvlogical工具可以用--slot和--create-slot选项创建逻辑槽,适合命令行操作。
还有一种不太常用但很实用的临时槽(temporary slot)。在函数的第三个参数传入true即可创建,会话断开后自动删除,不会残留:
SELECT * FROM pg_create_physical_replication_slot('tmp_slot', true);
删除复制槽的正确姿势与故障处理
删除复制槽使用pg_drop_replication_slot函数,但前提是槽必须处于非活跃状态。如果备库还在线并占用着这个槽,删除会直接报错:
SELECT pg_drop_replication_slot('standby1_slot');
-- 报错信息:replication slot "standby1_slot" is active for PID 12345
遇到活跃槽删不掉的情况,需要先确认是什么进程在使用它。可以在主库执行SELECT pid, usename, application_name, state FROM pg_stat_replication;查看复制连接。如果确认业务已经下线、连接是僵尸状态,可以使用pg_terminate_backend(pid)强制断开对应连接,再执行删除。千万不要为了删槽直接重启数据库,这属于杀鸡用牛刀且会带来不必要的停机。
最经典的故障是失效槽导致磁盘占满。排查思路是:先看pg_replication_slots中active = false但restart_lsn长期不推进的槽,计算它保留的WAL大小;再检查pg_wal目录的磁盘占用。处理办法是立即删除失效槽,让主库释放WAL。如果磁盘已经满到无法连接,可以在单用户模式下或者先清理其他文件腾出空间再操作。
预防层面,PostgreSQL 13引入的max_slot_wal_keep_size参数非常关键,它给单个槽能保留的WAL总量设了上限:
# postgresql.conf max_slot_wal_keep_size = 20GB
超过上限后槽会进入lost状态,WAL被正常回收,虽然该槽对应的备库需要重建,但主库磁盘安全得到了保障。建议所有生产环境都显式设置这个参数,而不是依赖默认的无限保留。同时可以部署监控脚本,定期检查pg_replication_slots中非活跃槽的存留时间和保留日志量,做到早发现早处理,从根本上避免复制槽引发的磁盘故障。
PostgreSQL复制槽slot管理修改时间:2026-09-01 08:40:57