目录一、事故现象1 存储空间持续下降2 WAL日志持续增长3 复制槽滞后不断增加二、复制槽是什么三、复制槽为什么会变成僵尸1 逻辑复制2 手动创建复制槽3 同步任务异常退出四、为什么僵尸复制槽会写满磁盘五、如何快速排查复制槽问题1 查看所有复制槽2 查找僵尸复制槽3 查看复制连接4 查看单个复制槽占用六、解决方法删除复制槽七、事故溯源八、如何预防1 监控复制槽指标2 限制复制槽数量3 限制复制槽 WAL 大小九、参数配置示例自建 PostgreSQLAWS Aurora PostgreSQL十、事故总结1 对 PostgreSQL 机制理解不足2 监控体系不完善3 故障排查经验不足十一、经验总结很多 PostgreSQL 的线上事故其实不是数据量暴涨也不是慢 SQL而是一个容易被忽视的机制Replication Slot复制槽在一次数据库告警中我们遇到了一个典型问题数据库磁盘持续上涨最终接近写满。排查后发现罪魁祸首是一个“僵尸复制槽”。这篇文章记录完整排查过程以及如何预防类似事故。一、事故现象某天数据库监控出现异常1 存储空间持续下降监控指标FreeStorageSpace可用存储空间持续减少。但奇怪的是业务数据没有明显增长表空间占用正常2 WAL日志持续增长查看数据库指标TransactionLogsDiskUsage事务日志磁盘使用量持续上涨。对于Aurora PostgreSQL该指标只有在以下场景才会出现Logical ReplicationAWS DMS否则默认返回-13 复制槽滞后不断增加指标OldestReplicationSlotLag含义最滞后的复制槽未消费的 WAL 数据量。如果该指标持续增长说明有复制槽没有消费 WAL。二、复制槽是什么PostgreSQL 中有一个重要机制Replication Slot复制槽它的作用是保证下游复制任务在消费 WAL 之前主库不会删除 WAL。换句话说如果复制任务没有消费 WAL那么WAL 文件就会一直保留。三、复制槽为什么会变成僵尸复制槽通常在以下场景创建1 逻辑复制例如DebeziumCanalAWS DMSPostgreSQL Logical Replication2 手动创建复制槽例如SELECT * FROM pg_create_logical_replication_slot(...)3 同步任务异常退出例如CDC 程序崩溃数据同步任务停止测试任务结束此时复制槽仍然存在但没有消费者。于是就会出现Zombie Replication Slot僵尸复制槽四、为什么僵尸复制槽会写满磁盘PostgreSQL 的 WAL 回收逻辑只要复制槽没有消费 WALWAL 就不会被删除因此复制槽停止消费 ↓ WAL持续生成 ↓ WAL无法回收 ↓ 磁盘被写满五、如何快速排查复制槽问题1 查看所有复制槽SELECT slot_name, slot_type, plugin, database, active, active_pid, restart_lsn, confirmed_flush_lsn, pg_size_pretty( pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) ) AS retained_wal FROM pg_replication_slots;重点关注active retained_wal2 查找僵尸复制槽SELECT slot_name, slot_type, active, pg_size_pretty( pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) ) AS retained_wal FROM pg_replication_slots WHERE active false ORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC;判断标准active false WAL占用很大基本可以确认僵尸复制槽3 查看复制连接SELECT pid, application_name, state, client_addr, backend_start FROM pg_stat_replication;如果pg_stat_replication 没有连接 且 slot active false说明复制任务已经不存在。4 查看单个复制槽占用SELECT slot_name, pg_size_pretty( pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) ) AS retained_wal FROM pg_replication_slots WHERE slot_name your_slot_name;六、解决方法如果确认复制槽已经不再使用可以直接删除。注意必须在主库执行。删除复制槽SELECT pg_drop_replication_slot(slot_name);删除后WAL 会立刻开始回收存储空间迅速释放七、事故溯源此次事故主要发生在测试环境。问题原因测试创建了复制槽同步任务异常退出复制槽没有被清理PostgreSQL 有一个问题pg_replication_slots不记录创建时间不记录创建用户因此溯源较困难。只能通过slot_nameplugindatabase进行推测。八、如何预防这类事故其实很常见生产环境必须做好以下措施。1 监控复制槽指标建议监控指标说明FreeStorageSpace剩余存储OldestReplicationSlotLag复制槽滞后TransactionLogsDiskUsageWAL占用建议设置告警OldestReplicationSlotLag 10GB2 限制复制槽数量参数max_replication_slots含义最大允许创建的复制槽数量。修改后需要重启数据库。3 限制复制槽 WAL 大小参数max_slot_wal_keep_size含义单个复制槽最多保留多少 WAL。超过后复制任务会报错停止。该参数无需重启即可生效。九、参数配置示例自建 PostgreSQL进入目录需要根据个人的安装情况。/var/lib/pgsql/15/data修改postgresql.conf配置示例max_replication_slots 10 max_slot_wal_keep_size 10GB重启数据库systemctl restart postgresql-15.serviceAWS Aurora PostgreSQL通过Parameter Group修改max_replication_slots max_slot_wal_keep_size十、事故总结本次事故暴露了三个问题1 对 PostgreSQL 机制理解不足第一次使用 PostgreSQL对Replication Slot 机制理解不够深入。2 监控体系不完善缺少以下关键监控复制槽滞后WAL增长导致问题早期没有被发现。3 故障排查经验不足排查过程中优先检查了表空间业务数据没有第一时间想到复制槽导致 WAL 堆积。十一、经验总结在 PostgreSQL 中复制槽是最容易导致磁盘被写满的机制之一。生产环境必须做到限制复制槽数量限制WAL保留监控复制槽滞后否则一旦同步任务异常退出WAL 会持续增长直到写满磁盘。