WalMiner
功能描述
WalMiner是从WAL(write ahead logs)日志中解析出执行的SQL语句的工具,并能生成对应的undo SQL语句。与传统的logical decode插件相比,WalMiner不要求logical日志级别且解析方式较为灵活。目前WalMiner支持解析系统表的变化,可以分析出DDL语句引起的系统表的变化。详细功能如下:
从产生WAL日志的数据库中直接执行解析。包含以下两个方面:
- 能够恢复DATABASE删除后的数据(数据字典完整,WAL 完整,操作发生后即刻停库),即被删除 DATABASE内的所有用户或部分数据(根据可恢复条件)。
- 能够恢复SCHEMA删除后的数据(数据字典完整,WAL 完整,操作发生后即刻停库),即被删除SCHEMA下属的所有用户数据(根据可恢复条件)。
从非 WAL产生的数据库中执行 WAL日志解析。
DDL 解析。支持DROP TABLE,VACUUM FULL,TRUNCATETABLE等DDL语句。对应场景能够识别此类 DDL,并能够恢复因此操作而被删除的用户数据表数据。
支持解析双写模式的WAL日志,要求wal_level设置为logical且目标表具有主键。
注意事项
针对数据字典不一致的情况,无法保证恢复完整性。
不支持开启线程池模式下运行WalMiner插件,即需要在配置文件中配置参数enable_thread_pool=off。
语法格式
添加需解析的WAL日志文件。
SELECT walminer_wal_add('<WAL日志文件路径>');
移除WAL日志文件。
SELECT walminer_wal_remove('<WAL日志文件路径>');
查询日志文件。
SELECT walminer_wal_list();
解析添加的全部WAL日志。
SELECT walminer_all();
或使用
SELECT wal2sql();
在添加的WAL日志中查找对应时间范围的WAL记录。
SELECT walminer_by_time(starttime,endtime,['true']);
或使用
SELECT wal2sql(starttime,endtime,['true']);
在添加的WAL日志中查找对应LSN范围的WAL记录。
SELECT walminer_by_lsn(startlsn,endlsn,['true']);
或使用
SELECT wal2sql(startlsn,endlsn,['true']);
在添加的WAL日志中查找对应XID的WAL记录。
SELECT walminer_by_xid(xid,['true']);
或使用
SELECT wal2sql(xid,['true']);
'true'表示进行精确解析。WalMiner 的构建基础是,checkpoint 之后对每一个 page 的更改会产生全页写(FPW),因此一个checkpoint之后的所有 WAL日志可以完美解析。注意 checkpoint 是指checkpoint开始的点,而不是checkpoint的wal记录的点。 普通解析会直接解析给定范围内的WAL日志,因为可能没有找到之前的 checkpoint 点,所以会出现有些记录解析不全导致出现空的解析结果。 精确解析是指WalMiner程序会界定需要解析的WAL范围,并在给定的WAL范围之前探索一个checkpoint开始点c1,从c1点开始记录FPI,然后就可以完美解析指定的WAL范围。如果在给定的WAL段内没有找到c1点,那么此次解析会报错停止。
替身映射
SELECT walminer_table_avatar(avatar_table_name,missed_relfilenode);
如果一个表被DROP或者被TRUNCATE等操作,导致新产生的数据字典不包含旧的数据库中所包含的relfilenode,那么使用新的数据字典无法解析出旧的WAL日志中包含的的某些内容。在知晓旧表的表结构的前提下,可以使用替身解析模式。替身模式目前只适用于从 WAL日志产生的数据库中直接执行解析的场景。
查看解析结果。
SELECT * FROM walminer_contents;
walminer_contents表中字段信息如下:
字段名 | 数据类型 | 描述 |
---|---|---|
sqlno | integer | 本条SQL在其事务中的序号。 |
xid | bigint | 事务ID。 |
topxid | bigint | 父事务ID,为0表示该事务非子事务。 |
sqlkind | integer | SQL类型
|
minerd | boolean | 解析结果是否完整(缺失checkpoint情况下可能无法解析出正确结果。 |
timestamp | timestamp with time zone | SQL所在事务的提交时间。 |
op_text | text | SQL |
undo_text | text | UNDO SQL |
complete | boolean | SQL所在事务是否完整解析。 |
schema | text | 目标表所在模式。 |
relation | text | 目标表表名。 |
start_lsn | text | 该记录的开始LSN。 |
commit_lsn | text | 事务的提交LSN。 |
- 结束WalMiner操作
用于释放内存,结束日志分析。
SELECT walminer_stop();
示例
1、创建插件。
CREATE EXTENSION walminer;
2、手动归档老的日志,切换到新的日志。
SELECT pg_switch_xlog();
3、执行检查点,创建测试数据。
CHECKPOINT;
CREATE TABLE test_xx(col int);
INSERT INTO test_xx VALUES(998);
SELECT * FROM test_xx;
结果返回如下:
col
-----
998
(1 row)
4、模拟数据丢失,删除数据。
DELETE FROM test_xx;
5、添加日志文件到WalMiner准备解析。
SELECT walminer_wal_add('/home/vastbase/data/oracle/pg_xlog');
结果返回如下:
walminer_wal_add
---------------------
14 file add success
(1 row)
6、查询WalMiner中的日志文件。
SELECT walminer_wal_list();
结果返回如下:
(/home/vastbase/data/oracle/pg_xlog/000000010000000000000001)
(/home/vastbase/data/oracle/pg_xlog/000000010000000000000002)
(/home/vastbase/data/oracle/pg_xlog/000000010000000000000003)
(/home/vastbase/data/oracle/pg_xlog/000000010000000000000004)
(/home/vastbase/data/oracle/pg_xlog/000000010000000000000005)
(/home/vastbase/data/oracle/pg_xlog/000000010000000000000006)
(/home/vastbase/data/oracle/pg_xlog/000000010000000000000007)
(/home/vastbase/data/oracle/pg_xlog/000000010000000000000008)
(/home/vastbase/data/oracle/pg_xlog/000000010000000000000009)
(/home/vastbase/data/oracle/pg_xlog/00000001000000000000000A)
(/home/vastbase/data/oracle/pg_xlog/00000001000000000000000B)
(/home/vastbase/data/oracle/pg_xlog/00000001000000000000000C)
(/home/vastbase/data/oracle/pg_xlog/00000001000000000000000D)
(/home/vastbase/data/oracle/pg_xlog/00000001000000000000000E)
(14 rows)
7、执行全部解析。
SELECT walminer_all();
结果返回如下:
NOTICE: table "walminer_contents" does not exist, skipping
CONTEXT: SQL statement "DROP TABLE IF EXISTS walminer_contents"
PL/pgSQL function public.walminer_contents_check() line 3 at SQL statement
referenced column: walminer_contents_check
SQL function "walminer_all" statement 1
referenced column: walminer_all
NOTICE: Switch wal to 000000010000000000000001 on time 2023-09-05 10:55:42.137507+08
CONTEXT: referenced column: wal2sql_internal
SQL function "walminer_all" statement 2
referenced column: walminer_all
NOTICE: Switch wal to 000000010000000000000002 on time 2023-09-05 10:55:42.170179+08
CONTEXT: referenced column: wal2sql_internal
SQL function "walminer_all" statement 2
referenced column: walminer_all
NOTICE: Switch wal to 000000010000000000000003 on time 2023-09-05 10:55:42.19022+08
CONTEXT: referenced column: wal2sql_internal
SQL function "walminer_all" statement 2
referenced column: walminer_all
NOTICE: Switch wal to 000000010000000000000004 on time 2023-09-05 10:55:42.204866+08
CONTEXT: referenced column: wal2sql_internal
SQL function "walminer_all" statement 2
referenced column: walminer_all
walminer_all
---------------------
pg_minerwal success
(1 row)
8、查询解析数据。
SELECT * FROM walminer_contents;
结果返回如下:
sqlno | xid | topxid | sqlkind | minerd | timestamp | op_text | undo_text
| complete | schema | relation | start_lsn | commit_lsn
-------+-------+--------+---------+--------+------------------------+---------------------------------------------+---------------------------------------
------+----------+--------+----------+-----------+------------
1 | 17095 | 0 | 1 | t | 2000-01-01 08:00:00+08 | INSERT INTO public.test_xx(col) VALUES(998) | DELETE FROM public.test_xx WHERE col=9
98 | t | public | test_xx | 0/4002528 | 0/4002608
1 | 17096 | 0 | 3 | t | 2000-01-01 08:00:00+08 | DELETE FROM public.test_xx WHERE col=998 | INSERT INTO public.test_xx(col) VALUES
(998) | t | public | test_xx | 0/4002678 | 0/4002748
(2 rows)