记一次 PostgreSQL WAL 日志撑爆磁盘的排查

起因

阿里云 RDS PostgreSQL 磁盘满了。看了一下,WAL 日志从某个时间点开始一直在涨,没有回落过。业务数据量没什么变化,问题出在 WAL 本身。

排查

先查了 replication slot:

sql 复制代码
SELECT slot_name, database, active, restart_lsn, confirmed_flush_lsn
FROM pg_replication_slots;

发现有一个叫 test 的 slot,active = false,没人在用。

再查日志,找到了创建它的 SQL:

sql 复制代码
SELECT pg_create_logical_replication_slot('test', 'pgoutput');

时间点和 WAL 开始堆积的时间完全对得上。应该是有人测试逻辑复制,创建了 slot 之后忘了删。

原因

PostgreSQL 的 replication slot 会保证:在消费者确认读取之前,不删除对应的 WAL。这个机制本身是为了防止从库或 CDC 工具断连后丢数据。

但如果 slot 没人用了、也没人删,PostgreSQL 就会一直保留 WAL,永远不清理。磁盘自然就满了。

修复

删掉这个 slot:

sql 复制代码
SELECT pg_drop_replication_slot('test');

删完之后 PostgreSQL 自动清理了多余的 WAL,空间就释放了。

另外加了一个参数防止以后再出这种事:

ini 复制代码
max_slot_wal_keep_size = 10240  -- 10GB,大概是磁盘的 10%~20%

这个参数的意思是,如果某个 slot 保留的 WAL 超过这个值,PostgreSQL 会强制清理,slot 会失效。消费者需要重新全量同步,但至少不会把磁盘撑爆。

注意这个参数只管以后,已经堆积的 WAL 还是得靠删 slot 来释放。


顺便整理一下 Replication Slot 相关的知识

WAL 是什么

PostgreSQL 的所有数据变更都先写 WAL,再写数据文件。WAL 有三个用途:

  • 崩溃恢复:重启后重放 WAL 恢复数据一致性
  • 复制:把 WAL 发给从库,做主从同步
  • PITR(时间点恢复):配合基础备份回到任意时间点

正常情况下 WAL 被消费完就会清理,磁盘占用是稳定的。

Replication Slot 是什么

简单说就是一个书签,告诉 PostgreSQL:"从这个位置往后的 WAL 还有人要读,别删。"

没有 slot 的时候,从库断连后主库可能已经把 WAL 清理了,从库重连就只能全量重做。有了 slot,主库会记住每个消费者读到哪了,断连后再接上就行。

两种类型

  • 物理复制槽:用于流复制(主从同步),直接传 WAL 二进制,从库和主库版本要一致
  • 逻辑复制槽 :用于逻辑复制和 CDC,通过解码插件(pgoutput、wal2json 等)把 WAL 解析成 INSERT/UPDATE/DELETE,消费者可以是不同版本的 PG 甚至其他系统

什么场景会用到

逻辑复制:

PostgreSQL 自带的功能。比如跨版本迁移(PG 14 → PG 16)、只同步部分表到另一个库、跨地域同步。

sql 复制代码
-- 发布端
CREATE PUBLICATION my_pub FOR TABLE users, orders;

-- 订阅端(会自动创建 replication slot)
CREATE SUBSCRIPTION my_sub
  CONNECTION 'host=... dbname=...'
  PUBLICATION my_pub;

CDC(Change Data Capture):

把数据库变更实时同步到外部系统。比如用 Debezium 把变更推到 Kafka,再分发到 ES、数据湖之类的。

复制代码
PostgreSQL → Replication Slot → Debezium → Kafka → 下游系统

其他:

实时计算(Flink)、搜索索引更新、缓存失效通知,只要需要监听数据库变更的场景都可能用到。

WAL 是实例级别的,不是数据库级别的。

PostgreSQL 整个实例(所有数据库)共用一条 WAL 流,所有库的所有变更都顺序写入同一份 WAL 文件。Replication slot 记录的是一个 WAL 位置(restart_lsn),它告诉 PostgreSQL:"这个位置之后的 WAL 文件,一个都不能删。"

所以不管是哪个 database、哪个 schema 产生的写入,只要产生了新的 WAL 段,就会堆积在那个位置之后,全部被 slot 拦住,无法清理。

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码3 天前
别再前后端各写一套表单校验了
java·后端
大勇前进4 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
yuzhi_liu4 天前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile4 天前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go
大白804 天前
PHP 内存溢出排查思路:看懂报错日志,精准定位问题
后端
二月龙4 天前
PHP 接口返回统一响应封装,让前后端对接更省心
后端
盖伦发发4 天前
软件工程SOLID 五大设计原则
后端·软件工程