使用软件遇到问题可先更新代码(欢迎提交bug)~~
开发群组:811105058欢迎任何使用者
XlogMiner是从PostgreSQL的WAL(write ahead logs)日志中解析出执行的SQL语句的工具,并能生成出对应的undo SQL语句。此版本使用限制较大,需要将wal级别设置为logical,而且需要将表设置为IDENTITY FULL模式。这会加剧wal日志的膨胀,降低数据库性能。为迎合PG日志名称的改变,现将XlogMiner改名为WalMiner。新的开源地址为:https://gitee.com/movead/XLogMiner
WalMiner功能增强
1.WalMiner支持解析minimal级别以上的任何wal日志级别。
2.无需将表设置为IDENTITY FULL模式。
3.增加对系统表修改的wal记录的解析。
4.修复发生relfilenode变化后,异库解析失败的bug。
WalMiner带来的新的限制
walminer可以完整的解析出给定的wal日志中第一个checkpoint点之后的所有wal记录。第一个checkpoint点之前的delete和update记录可能会解析失败,如下。
UPDATE "public"."t1" SET VALUES(NULL) (NOTICE:wal is not enought.);
如果一定需要将此记录解析出来,那么只需要增加更早的wal日志即可。
使用方法一
1.编译安装
代码下载地址:https://gitee.com/movead/XLogMiner/releases
编译安装:将下载的代码中的walminer目录放置到数据库代码的contrib目录下,执行make;make install
[lichuancheng@IP43 walminer]$ pwd
/work/src/lichuancheng/postgresql-11.1/contrib/walminer
[lichuancheng@IP43 walminer]$ make;make install
2.创建extension
postgres=# create extension walminer;
CREATE EXTENSION
postgres=#
3.插入测试数据
postgres=# create table t2(i int,j int, k varchar);
CREATE TABLE
postgres=# insert into t2 values(1,1,'qqqqqq');
INSERT 0 1
postgres=# insert into t2 values(2,2,'wwwwww');
INSERT 0 1
postgres=# insert into t2 values(3,3,'eeeee');
INSERT 0 1
postgres=# update t2 set k = '1111qqqqq' where i = 1;
UPDATE 1
postgres=# delete from t2 where j = 2;
DELETE 1
postgres=# insert into t2 values(4,4,'44444rrrrrr');
INSERT 0 1
postgres=# select pg_walfile_name(pg_current_wal_lsn());
pg_walfile_name
--------------------------
00000001000000000000000A
(1 row)
postgres=# select pg_switch_wal();
pg_switch_wal
---------------
0/A003508
(1 row)
postgres=#
手动备份这个需要解析的wal日志(此处我没有开归档,做这个手动备份防止wal日志被数据库移除)
[lichuancheng@IP43 pg_wal]$ cp 00000001000000000000000A 111/
[lichuancheng@IP43 pg_wal]$
4.指定需要解析的wal日志
(wal日志可以放置在任意位置,也可同时指定多个wal日志)
postgres=# select walminer_wal_add('pg_wal/111/00000001000000000000000A');
NOTICE: Get data dictionary from current database.
walminer_wal_add
--------------------
1 file add success
(1 row)
5.执行解析
解析方法1:
postgres=# select walminer_start('NULL','NULL',0,0);
walminer_start
---------------------
walminer sucessful!
(1 row)
解析方法2:(此种解析在$PGDATA/walminer/temp目录下会记录系统表的变更情况)
postgres=# select walminer_start('NULL','NULL',0,0,true);
walminer_start
---------------------
walminer sucessful!
6.解析结果查看
postgres=# \x
Expanded display is on.
postgres=# select record_database,record_user,op_text,op_undo from walminer_contents;
-[ RECORD 1 ]---+------------------------------------------------------------------------------------------------------
record_database | postgres
record_user | lichuancheng
op_text | INSERT INTO "public"."t2"("i", "j", "k") VALUES(1, 1, 'qqqqqq');
op_undo | DELETE FROM "public"."t2" WHERE "i"=1 AND "j"=1 AND "k"='qqqqqq' AND ctid = '(0,1)';
-[ RECORD 2 ]---+------------------------------------------------------------------------------------------------------
record_database | postgres
record_user | lichuancheng
op_text | INSERT INTO "public"."t2"("i", "j", "k") VALUES(2, 2, 'wwwwww');
op_undo | DELETE FROM "public"."t2" WHERE "i"=2 AND "j"=2 AND "k"='wwwwww' AND ctid = '(0,2)';
-[ RECORD 3 ]---+------------------------------------------------------------------------------------------------------
record_database | postgres
record_user | lichuancheng
op_text | INSERT INTO "public"."t2"("i", "j", "k") VALUES(3, 3, 'eeeee');
op_undo | DELETE FROM "public"."t2" WHERE "i"=3 AND "j"=3 AND "k"='eeeee' AND ctid = '(0,3)';
-[ RECORD 4 ]---+------------------------------------------------------------------------------------------------------
record_database | postgres
record_user | lichuancheng
op_text | UPDATE "public"."t2" SET "k" = '1111qqqqq' WHERE "i"=1 AND "j"=1 AND "k"='qqqqqq';
op_undo | UPDATE "public"."t2" SET "k" = 'qqqqqq' WHERE "i"=1 AND "j"=1 AND "k"='1111qqqqq' AND ctid = '(0,4)';
-[ RECORD 5 ]---+------------------------------------------------------------------------------------------------------
record_database | postgres
record_user | lichuancheng
op_text | DELETE FROM "public"."t2" WHERE "i"=2 AND "j"=2 AND "k"='wwwwww';
op_undo | INSERT INTO "public"."t2"("i", "j", "k") VALUES(2, 2, 'wwwwww');
-[ RECORD 6 ]---+------------------------------------------------------------------------------------------------------
record_database | postgres
record_user | lichuancheng
op_text | INSERT INTO "public"."t2"("i", "j", "k") VALUES(4, 4, '44444rrrrrr');
op_undo | DELETE FROM "public"."t2" WHERE "i"=4 AND "j"=4 AND "k"='44444rrrrrr' AND ctid = '(0,5)';
postgres=#
使用方法二
可以使用测试数据库解析生产库的wal日志。
(1)需要使用select walminer_build_dictionary(PATH);在生产库生成数据字典,然后将生成的数据字典和需要解析的wal日志放到测试库可以获取的路径。
(2)在测试库使用select walminer_load_dictionary(PATH);加载数据字典
(3)指定wal日志->执行解析->查看解析结果
具体使用方法在开源代码中的readme中查看。
使用限制
1.本版本只解析DML语句,不处理DDL语句
未来改动:DDL语句的解析已放入todolist,可能会逐步支持各种DDL语句
2.执行了删除表、truncate表、更改表的表空间、更改表字段的类型、vacuum full,这样的DDL语句后,发生DDL语句之前的此表相关的DML语句不会再被解析。
应对措施:建议在执行表结构变更之前,先保存一份数据字典,用来保证可以解析历史wal日志。
未来改动:现在已经考虑在walminer内增加保存数据字典的功能
3.解析结果依赖于数据字典。(举例:创建表t1,所有者为user1,但是中间将所有者改为user2。那解析结果中,所有t1相关操作所有者都将标示为user2)
应对措施:建议在执行表结构变更之前,先保存一份数据字典,用来保证可以解析历史wal日志。
未来改动:现在已经考虑在walminer内增加保存数据字典的功能
4.解析结果中undo字段的ctid属性是发生变更“当时”的值,如果因为vacuum等操作导致ctid发生变更,这个值将不准确。对于有可能存在重复行的数据,
我们需要通过这个值确定undo对应的tuple条数,不代表可以直接执行该undo语句。
5.执行了表字段drop的DDL语句后,发生DDL语句之前的这个字段相关的值都会被解析为encode('AD976BC56F',hex)的形式,另外自定义类型也会解析为这种形式
6.只能解析与数据字典时间线一致的wal文件
7.WalMiner是个人出品,暂时未进行全面测试。
8.[20190309新增]不建议使用walminer解析大宗copy语句(在同一个事务中插入大量数据行)产生的wal日志,
这会导致解析过程中的效率低下和内存占用过高。
WalMiner的改进原理
旧版本的工具xlogminer的解析来源是当前wal record所记录的"变更点"。它不会从FPW里的page里获取数据进行wal记录的解析。而新版本的walminer不仅可以解析当前wal记录的FPW,而且还对解析过程中出现的所有FPW进行记录和redo。因此walminer可以解析低级别的wal日志,可以不需要将表设置为DENTITY FULL模式。
如下图所示,我们加载了从000000030000075100000050开始的很多wal日志。000000030000075100000050到00000003000007510000005A的wal日志可能会因为它依赖的wal日志不足而无法完全解析(表现为一些DML记录没有具体数据),在提示时间或LSN(2019-03-21 13:57:46+08 or 751/5a711300)之后的数据会完全解析。
联系我
发现bug或者有好的建议可以通过邮箱(lchch1990@sina.cn)联系我。