wal2json 的使用和注意事项
wal2json 的使用和注意事项
wal2json 是 PostgreSQL 的一个 逻辑解码插件,可以把 WAL 日志解析成 JSON 格式的 DML 变更记录,从而间接得到“可读的 SQL”。下面我分成 使用步骤 和 注意事项 两部分整理给你。
一、wal2json 的使用步骤
1. 启用逻辑解码
在 postgresql.conf 修改参数(需要重启数据库):
wal_level = logical
max_wal_senders = 4
max_replication_slots = 4
max_replication_connections = 4
如果数据库有大量写操作,最好调大 max_slot_wal_keep_size(防止 replication slot 没消费导致 WAL 爆仓)。
2. 确认插件已安装
在 PostgreSQL 的 contrib 目录通常有 wal2json。
SELECT * FROM pg_available_extensions WHERE name = 'wal2json';
如果没有,需要编译安装:
git clone https://github.com/eulerto/wal2json.git
cd wal2json
make && make install
3. 创建逻辑复制槽
SELECT * FROM pg_create_logical_replication_slot('myslot', 'wal2json');
4. 生成变更数据
随便执行一些 DML:
INSERT INTO emp(id, name) VALUES (1, 'Tom');
UPDATE emp SET name='Jerry' WHERE id=1;
DELETE FROM emp WHERE id=1;
5. 获取 WAL 变更并转 JSON
消费(返回 + 删除pg_logical_slot_get_changes)
SELECT * FROM pg_logical_slot_get_changes('myslot', NULL, NULL);
输出类似:
{"change":[{"kind":"insert","schema":"public","table":"emp","columnnames":["id","name"],"columnvalues":[1,"Tom"]}]}
{"change":[{"kind":"update","schema":"public","table":"emp","oldkeys":{"keynames":["id"],"keyvalues":[1]},"columnnames":["name"],"columnvalues":["Jerry"]}]}
{"change":[{"kind":"delete","schema":"public","table":"emp","oldkeys":{"keynames":["id"],"keyvalues":[1]}}]}
对应 SQL:
INSERT INTO public.emp (id, name) VALUES (1, 'Tom');
UPDATE public.emp SET name='Jerry' WHERE id=1;
DELETE FROM public.emp WHERE id=1;
pg_logical_slot_get_changes和pg_logical_slot_peek_changes
pg_logical_slot_get_changes() 的行为是一次性消费:
只要它成功返回一批 WAL 记录,PostgreSQL 会立即把该槽的 confirmed_flush_lsn 推进到这批记录的末尾,槽里已经返回的 WAL 就被认为“已处理”。因此
- 同一个 slot 再次调用时,已经没有更早的未消费记录;
- 除非期间又有新事务产生,否则结果集就是空的——这正是你看到“查一次就再也查不到”的原因
验证:
1.查 slot 当前位置
SELECT slot_name, confirmed_flush_lsn
FROM pg_replication_slots
WHERE slot_name = 'myslot_dbtest';
再产生一点新变更
INSERT INTO test VALUES (1);
SELECT * FROM pg_logical_slot_get_changes('myslot_dbtest', NULL, NULL);
就能看到仅包含刚才那条新插入的 JSON,而旧数据不会再出现。
pg_logical_slot_peek_changes
想要“反复查看”而不清空,怎么办?
用 只读接口 即可:
SELECT * FROM pg_logical_slot_peek_changes('myslot_dbtest', NULL, NULL);
peek_changes 不会推进槽位,可以多次执行,同一份 WAL 会反复返回,适合做调试或多次采样
总结
- get_changes = 消费(返回 + 删除)。
- peek_changes = 偷看(返回 + 保留)。
所以“查询一次之后数据就查询不到”是 正常且预期 的行为,并非配置错误或插件缺失
二、注意事项 ⚠️
1.插件要求
- PostgreSQL 9.4+ 支持逻辑解码;
- 建议 PostgreSQL 10+ 使用(对 wal2json 兼容性更好)。
2.事务粒度
- wal2json 会按事务输出,包含 begin、commit,在跨事务解析时要注意拼接。
3.性能影响
- 开启 wal_level=logical 会增加 WAL 大小;
- 逻辑复制槽如果长时间不消费,会导致 WAL 文件无法清理,可能撑爆磁盘。
- 一定要设置 max_slot_wal_keep_size 或及时调用 pg_drop_replication_slot。
4.主键要求
- UPDATE 和 DELETE 输出 oldkeys 时,需要表有 主键或 REPLICA IDENTITY。
- 没有主键时,建议:
ALTER TABLE emp REPLICA IDENTITY FULL;
否则无法生成删除/更新条件。
5.过滤选项
wal2json 有参数可过滤输出,比如:
SELECT * FROM pg_logical_slot_get_changes(
'myslot', NULL, NULL,
'include-types', '1',
'include-transaction', '1',
'pretty-print', '1'
);
include-types=1 输出列类型;
include-transaction=1 输出事务 id;
pretty-print=1 格式化 JSON。
6.输出转换
wal2json 输出 JSON,不是 SQL;需要业务侧写解析器,把 JSON 转换为 INSERT/UPDATE/DELETE 语句或直接同步到目标存储。
✅ 总结:
wal2json 的正确姿势是:
- 开启逻辑解码 → 创建 replication slot → 获取 JSON → 解析为 SQL。
- 注意清理 replication slot、表主键配置和 WAL 爆仓风险。
常见问题
复制槽一直不消费会带来的影响
如果创建了逻辑复制槽(slot)却一直不消费,PostgreSQL 会:
- 1.保留从该槽的 confirmed_flush_lsn 开始以后所有的 WAL 文件(哪怕这些事务早已提交、表早已更新)。
- 2.导致 pg_wal(或 pg_xlog)目录持续膨胀,把磁盘打满只是时间问题。
- 3.任何试图删除仍在槽需要的 WAL 的操作(如 pg_archivecleanup、wal-g delete)都会被 PostgreSQL 拒绝,并出现类似如下的日志。
ERROR: cannot remove file "000000010000000000000012" — still needed by replication slot "myslot_dbtest"
如何快速验证槽正在“拖住”多少 WAL
-- 1. 看槽当前位置
SELECT slot_name, restart_lsn, confirmed_flush_lsn,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS wal_held
FROM pg_replication_slots
WHERE slot_name = 'myslot_dbtest';
-- 2. 看磁盘上实际堆积了多少
SELECT file_name,
pg_size_pretty(sum(size)) AS total_size
FROM pg_ls_waldir()
WHERE file_name >= (SELECT restart_lsn FROM pg_replication_slots
WHERE slot_name = 'myslot_dbtest')
GROUP BY file_name
ORDER BY file_name;
如果 wal_held 从几百 MB 涨到几十 GB,就证明 WAL 正在堆积。
多久能把磁盘撑爆?
取决于业务写入量。举例:
- 业务平均 10 MB/s 写入
- 槽 3 天无人消费
→ 大约 2.5 TB 的 WAL 会留在 pg_wal,很容易把 2 TB 盘直接写满,实例随后进入 PANIC 或 read-only 状态。
监控/告警建议
- 1.对 pg_replication_slots.restart_lsn 与 pg_current_wal_lsn() 的差值做告警(阈值 1 GB/10 GB 视业务而定)。
- 2.对 pg_stat_archiver/pg_ls_waldir() 的文件数、总大小做磁盘级告警。
- 3.对 slot.active = f 且 wal_held > x 的槽直接发页——说明有人建完 slot 就忘了消费。
万一已经堆积,如何自救
1.确认真的不再需要该槽:
SELECT pg_drop_replication_slot('myslot_dbtest');
槽一删,PostgreSQL 会立即把不再需要的 WAL 标记为可删除,下次 checkpoint/autovacuum 会回收,磁盘空间回落。
2.如果还想保留槽,但想先“止血”:
- 临时把 max_slot_wal_keep_size(PG 13+)设为一个非零值,例如 10GB,到达上限后 PostgreSQL 会强制断开该槽并删除多余 WAL,代价是复制链断裂,需要重建槽。
3.磁盘已满导致无法启动:
- 手动挪走/挂载更大盘 → 启动 → 先删槽或立即消费 → 再收缩。
一句话总结
- 逻辑复制槽不消费 ≈ 磁盘定时炸弹;
- 要么持续消费,要么及时删除,务必加监控。

浙公网安备 33010602011771号