pg_walex让回滚如此丝滑
最小,最强,最快,最易上手的 PostgreSQL WAL 日志解析工具,用起来就德芙一样丝滑
pg_walex 1.1.0,把 PostgreSQL 的 WAL 从"只读的黑匣子"变成了"可逆的时间机器"。
摘要
本文介绍 PostgreSQL WAL 日志解析工具 pg_walex 1.1.0 的核心能力与实战用法。新版在原有 WAL 解析基础上,新增了 --rollback 数据回滚能力与 RESTful API 回滚接口,输出中补充了操作类型 type、lsn、timestamp 等关键信息,并增强了对 TRUNCATE 与 DROP 的回滚支持。文章从源码下载、编译安装讲起,依次演示了 DML 误删除数据的找回、DROP 表与 TRUNCATE 表的数据恢复,以及通过 HTTP 接口进行实时监控与回滚的完整流程。核心结论是:只要 WAL 还在,误删的数据就还有得救------pg_walex 让 PostgreSQL 的 WAL 从"只读的黑匣子"变成"可逆的时间机器"。
版本更新预览
- 输出增加了操作类型
type,lsn,timestamp --rollback+GET/POST /api/v1/rollback正向语句后面紧跟逆操作,可回放撤销- 增强对
truncate和drop的回滚能力 - 修复在低版本C编译器编译时strncopy告警的问题的问题
- 使用memcopy替代strncopy
- 实时监控 +
GET /api/v1/item
一、主要增加功能
--rollback支持数据回滚能力RESTful API回滚能力,可接入多个平台
二、源码下载地址
源码可从github gitee gitcode 下载
bash
git clone https://github.com/shaohuasong-fang/pg_walex.git
git clone https://gitee.com/developerhonor/pg_walex.git
git clone https://gitcode.com/sungsasong/pg_walex.git
三、编译安装
3.1 编译构建
bash
# make -j$(nproc)
cc -O2 -g -std=c11 -Wall -Wextra -Wno-unused-parameter -pthread -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -Iinclude -Isrc -c -o src/pwe_util.o src/pwe_util.c
cc -O2 -g -std=c11 -Wall -Wextra -Wno-unused-parameter -pthread -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -Iinclude -Isrc -c -o src/pwe_version.o src/pwe_version.c
cc -O2 -g -std=c11 -Wall -Wextra -Wno-unused-parameter -pthread -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -Iinclude -Isrc -c -o src/pwe_wal.o src/pwe_wal.c
cc -O2 -g -std=c11 -Wall -Wextra -Wno-unused-parameter -pthread -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -Iinclude -Isrc -c -o src/pwe_tuple.o src/pwe_tuple.c
cc -O2 -g -std=c11 -Wall -Wextra -Wno-unused-parameter -pthread -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -Iinclude -Isrc -c -o src/pwe_catalog.o src/pwe_catalog.c
cc -O2 -g -std=c11 -Wall -Wextra -Wno-unused-parameter -pthread -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -Iinclude -Isrc -c -o src/pwe_recon.o src/pwe_recon.c
cc -O2 -g -std=c11 -Wall -Wextra -Wno-unused-parameter -pthread -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -Iinclude -Isrc -c -o src/pwe_output.o src/pwe_output.c
cc -O2 -g -std=c11 -Wall -Wextra -Wno-unused-parameter -pthread -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -Iinclude -Isrc -c -o src/pwe_main.o src/pwe_main.c
cc -O2 -g -std=c11 -Wall -Wextra -Wno-unused-parameter -pthread -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -Iinclude -Isrc -c -o src/pwe_http.o src/pwe_http.c
cc -o pg_walex src/pwe_util.o src/pwe_version.o src/pwe_wal.o src/pwe_tuple.o src/pwe_catalog.o src/pwe_recon.o src/pwe_output.o src/pwe_main.o src/pwe_http.o -pthread
3.2 安装
bash
# make install PREFIX=/usr/local
install -d /usr/local/bin
install -m 0755 pg_walex /usr/local/bin/pg_walex
3.3 查看版本
bash
# pg_walex --version
pg_walex 1.1.0
四、验证基本的DML回滚功能
4.1 建库
SQL
# psql -U postgres -d postgres -p6432
psql (18.4)
Type "help" for help.
postgres=# create database db_rollback;
CREATE DATABASE
4.2 确保wal_level = logical
SQL
db_rollback=# show wal_level ;
wal_level
-----------
logical
(1 row)
4.3 查看LSN和对应的文件
SQL
db_rollback=# SELECT pg_walfile_name(pg_current_wal_lsn()),pg_current_wal_lsn();
pg_walfile_name | pg_current_wal_lsn
--------------------------+--------------------
0000000100000005000000BB | 5/BB47AF10
(1 row)
4.4 模拟数据表
SQL
db_rollback=# CREATE TABLE tab_rollback(id int primary key,name varchar(20));
CREATE TABLE
db_rollback=# INSERT INTO tab_rollback VALUES(1,'Verifying rollback');
INSERT 0 1
4.5 再次查看LSN和对应的文件
SQL
db_rollback=# SELECT pg_walfile_name(pg_current_wal_lsn()),pg_current_wal_lsn();
pg_walfile_name | pg_current_wal_lsn
--------------------------+--------------------
0000000100000005000000BB | 5/BB47DAC8
(1 row)
4.6 模拟误删除数据
SQL
db_rollback=# DELETE FROM tab_rollback WHERE id = 1;
DELETE 1
4.7 查看日志LSN和对应的文件
SQL
db_rollback=# SELECT pg_walfile_name(pg_current_wal_lsn()),pg_current_wal_lsn();
pg_walfile_name | pg_current_wal_lsn
--------------------------+--------------------
0000000100000005000000BB | 5/BB47DB38
(1 row)
4.8 找误删除的数据
这里通过起始LSN 5/BB47DAC8 和结束LSN 5/BB47DB38的区间找
SQL
[root@PGServer ~]# pg_walex -f /data/tmp/data/pg_wal/0000000100000005000000BB --rollback -s 5/BB47DAC8 -e 5/BB47DB38
\connect db_rollback
-- pg_walex: type=delete,xid=1233,lsn=5/BB47DAC8,timestamp=2026-10-27 08:02:10.117709+00
BEGIN;
DELETE FROM public.tab_rollback WHERE id IS NOT DISTINCT FROM 1;
-- pg_walex: rollback,xid=1233,lsn=5/BB47DAC8,timestamp=2026-10-27 08:02:10.117709+00
INSERT INTO public.tab_rollback (id, name) VALUES (1, 'Verifying rollback');
COMMIT;
五、验证DROP和TRUNCATE的回滚能力
说明: 注意删除操作和TRUNCATE的恢复受限于缓存大小,如果误删除数据,请尽快停止写入数据库
5.1 验证DROP回滚能力
5.1.1 查看LSN及文件位置
db_rollback=# SELECT pg_walfile_name(pg_current_wal_lsn()),pg_current_wal_lsn();
pg_walfile_name | pg_current_wal_lsn
--------------------------+--------------------
0000000100000005000000BB | 5/BB47DC78
(1 row)
5.1.2 模拟表及插入5条数据
SQL
db_rollback=# CREATE TABLE tab_drop(id int primary key,name varchar(20));
CREATE TABLE
db_rollback=# INSERT INTO tab_drop VALUES(1,'first');
INSERT 0 1
db_rollback=# INSERT INTO tab_drop VALUES(2,'second');
INSERT 0 1
db_rollback=# INSERT INTO tab_drop VALUES(3,'third');
INSERT 0 1
db_rollback=# INSERT INTO tab_drop VALUES(4,'forth');
INSERT 0 1
db_rollback=# INSERT INTO tab_drop VALUES(5,'fifth');
INSERT 0 1
5.1.3 执行删除表操作
SQL
-- 删除前的日志序列号,用作DROP 操作的起始 LSN
db_rollback=# SELECT pg_walfile_name(pg_current_wal_lsn()),pg_current_wal_lsn();
pg_walfile_name | pg_current_wal_lsn
--------------------------+--------------------
0000000100000005000000BB | 5/BB4A89A0
(1 row)
db_rollback=# DROP TABLE tab_drop ;
DROP TABLE
5.1.4 查看LSN及文件位置
SQL
db_rollback=# SELECT pg_walfile_name(pg_current_wal_lsn()),pg_current_wal_lsn();
pg_walfile_name | pg_current_wal_lsn
--------------------------+--------------------
0000000100000005000000BB | 5/BB4ACB68
(1 row)
5.1.5 WAL 回滚 DROP 的能力
SQL
[root@PGServer ~]# pg_walex -f /data/tmp/data/pg_wal/0000000100000005000000BB --rollback -s 5/BB4A89A0 -e 5/BB4ACB68
\connect db_rollback
-- pg_walex: type=drop,xid=1240,lsn=5/BB4ABB08,timestamp=2026-10-27 08:11:09.278194+00
BEGIN;
DROP TABLE public.tab_drop;
-- pg_walex: rollback,xid=1240,lsn=5/BB4AC640,timestamp=2026-10-27 08:11:09.278194+00
CREATE TABLE public.tab_drop (
id int4 NOT NULL,
name varchar(20),
PRIMARY KEY (id)
);
INSERT INTO public.tab_drop (id, name) VALUES (3, 'third');
INSERT INTO public.tab_drop (id, name) VALUES (2, 'second');
INSERT INTO public.tab_drop (id, name) VALUES (1, 'first');
INSERT INTO public.tab_drop (id, name) VALUES (5, 'fifth');
INSERT INTO public.tab_drop (id, name) VALUES (4, 'forth');
COMMIT;
5.2 验证 TRUNCATE 回滚能力
5.2.1 模拟数据
SQL
db_rollback=# CREATE TABLE tab_truncate(id int primary key,name varchar(20));
CREATE TABLE
db_rollback=# INSERT INTO tab_truncate (id, name) VALUES
(1, 'Alice'),
(2, 'Bob'),
(3, 'Charlie'),
(4, 'Diana'),
(5, 'Eve');
INSERT 0 5
5.2.2 查看LSN及文件位置
SQL
db_rollback=# SELECT pg_walfile_name(pg_current_wal_lsn()),pg_current_wal_lsn();
pg_walfile_name | pg_current_wal_lsn
--------------------------+--------------------
0000000100000005000000BB | 5/BB4D55D0
(1 row)
####5.2.3 清空数据
SQL
db_rollback=# truncate table tab_truncate ;
TRUNCATE TABLE
5.2.4 查看LSN及文件位置
SQL
db_rollback=# SELECT pg_walfile_name(pg_current_wal_lsn()),pg_current_wal_lsn();
pg_walfile_name | pg_current_wal_lsn
--------------------------+--------------------
0000000100000005000000BB | 5/BB4D9E30
(1 row)
5.2.5 TRUNCATE 回滚能力验证
SQL
[root@PGServer ~]# pg_walex -f /data/tmp/data/pg_wal/0000000100000005000000BB --rollback -s 5/BB4D55D0 -e 5/BB4D9E30
\connect db_rollback
-- pg_walex: type=truncate,xid=1243,lsn=5/BB4D9AA8,timestamp=2026-10-27 08:18:03.800537+00
BEGIN;
TRUNCATE TABLE public.tab_truncate;
-- pg_walex: rollback,xid=1243,lsn=5/BB4D9CF8,timestamp=2026-10-27 08:18:03.800537+00
INSERT INTO public.tab_truncate (id, name) VALUES (3, 'Charlie');
INSERT INTO public.tab_truncate (id, name) VALUES (2, 'Bob');
INSERT INTO public.tab_truncate (id, name) VALUES (1, 'Alice');
INSERT INTO public.tab_truncate (id, name) VALUES (5, 'Eve');
INSERT INTO public.tab_truncate (id, name) VALUES (4, 'Diana');
COMMIT;
综上: 对于DROP和TRUNCATE的表,也可以通过WAL日志找回来。
六. RESTful API 支持
6.1 启动
SQL
[root@PGServer ~]# pg_walex --serve --monitor --listen 0.0.0.0 --port 8900 \
--token my-secret-token --dir /data/tmp/data/pg_wal
pg_walex 1.1.0 REST API listening on http://0.0.0.0:8900/api/v1
pg_walex: default WAL directory: /data/tmp/data/pg_wal
pg_walex: monitoring /data/tmp/data/pg_wal; latest statement at /api/v1/item
6.2 验证从指定LSN开始的监控
SQL
[root@PGServer ~]# curl -s -H "Authorization: Bearer my-secret-token" 'http://127.0.0.1:8900/api/v1/sql?dir=/data/tmp/data/pg_wal&start_lsn=5/BB4D55D0'
\connect db_rollback
-- pg_walex: type=truncate,xid=1243,lsn=5/BB4D9AA8,timestamp=2026-10-27 08:18:03.800537+00
BEGIN;
TRUNCATE TABLE public.tab_truncate;
COMMIT;
6.3 验证回滚的API能力
SQL
[root@PGServer ~]# curl -s -H "Authorization: Bearer my-secret-token" \
'http://127.0.0.1:8900/api/v1/rollback?dir=/data/tmp/data/pg_wal&start_lsn=5/BB4D55D0'
\connect db_rollback
-- pg_walex: type=truncate,xid=1243,lsn=5/BB4D9AA8,timestamp=2026-10-27 08:18:03.800537+00
BEGIN;
TRUNCATE TABLE public.tab_truncate;
-- pg_walex: rollback,xid=1243,lsn=5/BB4D9CF8,timestamp=2026-10-27 08:18:03.800537+00
INSERT INTO public.tab_truncate (id, name) VALUES (3, 'Charlie');
INSERT INTO public.tab_truncate (id, name) VALUES (2, 'Bob');
INSERT INTO public.tab_truncate (id, name) VALUES (1, 'Alice');
INSERT INTO public.tab_truncate (id, name) VALUES (5, 'Eve');
INSERT INTO public.tab_truncate (id, name) VALUES (4, 'Diana');
COMMIT;
总之
pg_walex让你:能读 、 能还原 、 能后悔 、 能实时
让你的 DROP 不再是终点,TRUNCATE 不再靠祈祷------WAL 还在,就还有得救。