pg_walex让回滚如此丝滑:PostgreSQL WAL 日志解析与数据回滚实战

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 还在,就还有得救。

相关推荐
ss2732 小时前
PostgreSQL事务隔离级别与案例
数据库·postgresql
严同学正在努力2 小时前
PostgreSQL 高可用教程(流复制 + Patroni 生产级实战)
运维·数据库·ai·postgresql·架构
wno7043 小时前
Spring Boot中JdbcTemplate与Mybatis使用PostgreSQL
spring boot·postgresql·mybatis
auspicious航17 小时前
PostgreSQL的备份与恢复
数据库·postgresql
IvorySQL18 小时前
PostgreSQL 日报| WAL summarizer 断言失败(10 月 26 日)
数据库·postgresql·区块链
ss2731 天前
30 PostgreSQL ACID特性详解
数据库·postgresql
严同学正在努力1 天前
PostgreSQL 数据库安装部署教程(Linux 多发行版 + Windows + Docker 全覆盖)
linux·数据库·postgresql
唐青枫1 天前
PostgreSQL 基本操作详解
postgresql
风哥2号2 天前
数据库教程FGMT69:PostgreSQL运维诊断与监控分析
运维·数据库·postgresql