PG日志挖掘工具之WalMiner用于误操作恢复
Tags: PGPostgreSQLWalMiner日志挖掘误操作恢复
PG中通过pg_waldump来分析pg_wal日志:https://www.xmmup.com/pgzhongtongguopg_waldumplaifenxipg_walrizhi.html
WalMiner简介
官网:https://gitee.com/movead/XLogMiner
WalMiner是从PostgreSQL的WAL(write ahead logs)日志中解析出执行的SQL语句的工具,并能生成对应的undo SQL语句。与传统的logical decode插件相比,walminer不要求logical日志级别且解析方式较为灵活。
WalMiner是从PostgreSQL的WAL(write ahead logs)日志的解析工具,旨在挖掘wal日志所有的有用信息,从而提供PG的数据恢复支持。目前主要有如下功能:
从waL日志中解析出SQL,包括DML和少量DDL
解析出执行的SQL语句的工具,并能生成对应的undo SQL语句。与传统的logical decode插件相比,walminer不要求logical日志级别且解析方式较为灵活。
数据页挽回
当数据库被执行了TRUNCATE等不被wal记录的数据清除操作,或者发生磁盘页损坏,可以使用此功能从wal日志中搜索数据,以期尽量挽回数据。
XlogMiner Enhancements Released and Renamed to WalMiner:https://www.postgresql.org/about/news/xlogminer-enhancements-released-and-renamed-to-walminer-1919/
注意:walminer是Highgo开源的一款软件。HGDB所有版本均支持walminer。
walminer3.0更新内容
- 代码结构变化
- 增加数据页挽回功能(坏块修复)
- 增加对开发库PG14的支持
walminer4.0介绍
walminer是PostgreSQL的wal日志解析工具,4.0版本摒弃插件模式改为bin模式,现已脱离对目标数据库的编译依赖和安装依赖,一个walminer工具可以解析PG10~PG15的WAL日志。 现已实现的功能为wal2sql、fosync、pgto。
1.1 walminer功能介绍
- wal2sql为walminer的基本功能,可以将wal日志解析为SQL语句,以及其undo语句,可解析部分DDL语句。
- fosync为在wal2sql的基础上实现的,PG故障转移延迟数据同步功能。
- pgto为PG的CDC工具,库外解析;一键部署;低wal级别。
1.2 walminer导航
PG版本支持
- walminer3.0支持PostgreSQL 10及其以上版本。(此版本放弃对9.x的支持)
编译安装
编译一:PG源码编译
如果你从编译pg数据库开始:
- 将walminer目录放置到编译通过的PG工程的"../contrib/"目录下
- 进入walminer目录
- 执行命令1make && make install
编译二:依据PG安装编译
如果你使用yum或者pg安装包已经编译安装了pg
配置pg的bin路径至环境变量
12export PGHOME=/pg13/pg13export PATH=$PGHOME/bin:$PATH:.进入walminer代码路径
12unzip XLogMiner-master.zipcd ./XLogMiner-master/walminer执行编译安装,注意修改MAJORVERSION的值,MAJORVERSION支持‘10’,‘11’,‘12’,‘13’
12USE_PGXS=1 MAJORVERSION=13 makeUSE_PGXS=1 MAJORVERSION=13 make install过程:
1234567891011121314151617181920212223[pg13@lhrpg walminer]$ USE_PGXS=1 MAJORVERSION=13 makegcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o walminer.o walminer.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o wm_utils.o wm_utils.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o datadictionary.o datadictionary.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o fetchcatalogtable.o fetchcatalogtable.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o wallist.o wallist.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o walreader.o walreader.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o walminer_decode.o walminer_decode.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o imagemanage.o imagemanage.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o wal2sql.o wal2sql.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o walminer_contents.o walminer_contents.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o walminer_thread.o walminer_thread.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o wal2sql_spi.o wal2sql_spi.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o wal2sql_ddl.o wal2sql_ddl.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -DPG_VERSION_13 -I. -I./ -I/pg13/pg13/include/postgresql/server -I/pg13/pg13/include/postgresql/internal -D_GNU_SOURCE -c -o pagecollect.o pagecollect.cgcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -fPIC -shared -o walminer.so walminer.o wm_utils.o datadictionary.o fetchcatalogtable.o wallist.o walreader.o walminer_decode.o imagemanage.o wal2sql.o walminer_contents.o walminer_thread.o wal2sql_spi.o wal2sql_ddl.o pagecollect.o -L/pg13/pg13/lib -Wl,--as-needed -Wl,-rpath,'/pg13/pg13/lib',--enable-new-dtags[pg13@lhrpg walminer]$ USE_PGXS=1 MAJORVERSION=13 make install/usr/bin/mkdir -p '/pg13/pg13/lib/postgresql'/usr/bin/mkdir -p '/pg13/pg13/share/postgresql/extension'/usr/bin/mkdir -p '/pg13/pg13/share/postgresql/extension'/usr/bin/install -c -m 755 walminer.so '/pg13/pg13/lib/postgresql/walminer.so'/usr/bin/install -c -m 644 .//walminer.control '/pg13/pg13/share/postgresql/extension/'/usr/bin/install -c -m 644 .//walminer--3.0.sql '/pg13/pg13/share/postgresql/extension/'
使用方法-SQL解析
场景一:从WAL日志产生的数据库中直接执行解析
1. 创建walminer的extension
1 | create extension walminer; |
2. 添加要解析的wal日志文件
1 2 3 | -- 添加wal文件: select walminer_wal_add('/pg13/pgdata/pg_wal'); -- 注:参数可以为目录或者文件 |
3. Remove wal日志文件
1 2 3 | -- 移除wal文件: select walminer_wal_remove('/opt/test/wal'); -- 注:参数可以为目录或者文件 |
4. List wal日志文件
1 2 | -- 列出wal文件: select walminer_wal_list(); |
5. 执行解析
5.1 普通解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | -- 解析add的全部wal日志 select walminer_all(); -- 或 select wal2sql(); -- 在add的wal日志中查找对应时间范围的wal记录 -- 可以参照walminer_time.sql回归测试中的使用用例 select walminer_by_time(starttime, endtime); 或 select wal2sql(starttime, endtime); -- 在add的wal日志中查找对应lsn范围的wal记录 -- 可以参照walminer_lsn.sql回归测试中的使用用例 select walminer_by_lsn(startlsn, endlsn); 或 select wal2sql(startlsn, endlsn); -- 在add的wal日志中查找对应xid的wal记录 -- 可以参照walminer_xid.sql回归测试中的使用用例 -- 前一个walminer版本对xid的支持是范围解析,但是xid的提交是不连续的 -- 会导致各种问题,所以这个版本只支持单xid解析 select walminer_by_xid(xid); 或 select wal2sql(xid); |
5.2 精确解析
1 2 3 4 5 6 7 8 9 10 11 12 | -- 在add的wal日志中查找对应时间范围的wal记录 select walminer_by_time(starttime, endtime,'true'); 或 select wal2sql(starttime, endtime,'true'); -- 在add的wal日志中查找对应lsn范围的wal记录 select walminer_by_lsn(startlsn, endlsn,'true'); 或 select wal2sql(startlsn, endlsn,'true'); -- 在add的wal日志中查找对应xid的wal记录 select walminer_by_xid(xid,'true'); 或 select wal2sql(xid,'true'); |
walminer的构建基础是,checkpoint之后对每一个page的更改会产生全页写(FPW),因此一个checkpoint之后的所有wal日志可以完美解析。注意checkpoint是指checkpoint开始的点,而不是checkpoint的wal记录的点,参照说明
普通解析会直接解析给定范围内的wal日志,因为可能没有找到之前的checkpoint点,所以会出现有些记录解析不全导致出现空的解析结果。
精确解析是指walminer程序会界定需要解析的wal范围,并在给定的wal范围之前探索一个checkpoint开始点c1,从c1点开始记录FPI,然后就可以完美解析指定的wal范围。如果在给定的wal段内没有找到c1点,那么此次解析会报错停止。
5.3 单表解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | -- 在add的wal日志中查找对应时间范围的wal记录 select walminer_by_time(starttime, endtime,'false',reloid); 或 select wal2sql(starttime, endtime,'true',reloid); -- 在add的wal日志中查找对应lsn范围的wal记录 select walminer_by_lsn(startlsn, endlsn,'true',reloid); 或 select wal2sql(startlsn, endlsn,'false',reloid); -- 在add的wal日志中查找对应xid的wal记录 select walminer_by_xid(xid,'true',reloid); 或 select wal2sql(xid,'true',reloid); |
'true'和‘false’代表是否为精确解析模式,reloid为目标表的oid(注意不是relfilenode)
5.4 快捷解析
场景1中的加载数据字典和加载wal日志步骤可以省略,默认直接加载当前数据字典和当前wal路径下的所有wal文件。这个解析模式只在学习本工具时使用,在生产数据库中,可能会因为wal段切换而导致解析失败。
5.5 替身解析
如果一个表被drop或者被truncate等操作,导致新产生的数据字典不包含旧的数据库中所包含的relfilenode,那么使用新的数据字典无法解析出旧的wal日志中包含的的某些内容。在知晓旧表的表结构的前提下,可以使用替身解析模式。替身模式目前只适用于[场景一]。
1 2 3 4 5 6 7 8 | -- 假设表t1被执行了vacuum full,执行vacuum full前的relfilenode为16384 -- 新建表t1的替身表 create table t1_avatar(i int); -- 执行替身映射 select walminer_table_avatar(avatar_table_name, missed_relfilenode); -- 执行解析 select wal2sql(); -- 查看解析结果时,会发现,对t1表的数据都以t1_avatar表的形式展现在输出结果中 |
6. 解析结果查看
1 | select * from walminer_contents; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | -- 表walminer_contents ( sqlno int, -- 本条sql在其事务内的序号 xid bigint, -- 事务ID topxid bigint, -- 如果为子事务,这是是其父事务;否则为0 sqlkind int, -- sql类型1->insert;2->update;3->delete(待优化项目) minerd bool, -- 解析结果是否完整(缺失checkpoint情况下可能无法解析出正确结果) timestamp timestampTz, --这个SQL所在事务提交的时间 op_text text, -- sql undo_text text, -- undo sql complete bool, -- 如果为false,说明有可能这个sql所在的事务是不完整解析的 schema text, -- 目标表所在的模式 relation text, -- 目标表表名 start_lsn pg_lsn, -- 这个记录的开始LSN commit_lsn pg_lsn -- 这个事务的提交LSN ) |
:warning: 注意:walminer_contents是walminer自动生成的unlogged表(之前是临时表,由于临时表在清理上有问题,引起工具使用不便,所以改为unlogged表),在一次解析开始会首先创建或truncate walminer_contents表。
7. 结束walminer操作
该函数作用为释放内存,结束日志分析,该函数没有参数。
1 | select walminer_stop(); |
场景二:从非WAL产生的数据库中执行WAL日志解析
:warning: 要求执行解析的PostgreSQL数据库和被解析的为同一版本
于生产数据库
1.创建walminer的extension
1 | create extension walminer; |
2.生成数据字典
1 2 | select walminer_build_dictionary('/opt/proc/store_dictionary'); -- 注:参数可以为目录或者文件 |
于测试数据库
1. 创建5walminer的extension
1 | create extension walminer; |
2. load数据字典
1 2 | select walminer_load_dictionary('/opt/test/store_dictionary'); -- 注:参数可以为目录或者文件 |
3. add wal日志文件
1 2 3 | -- 增加wal文件: select walminer_wal_add('/opt/test/wal'); -- 注:参数可以为目录或者文件 |
4. remove wal日志文件
1 2 3 | -- 移除wal文件: select walminer_wal_remove('/opt/test/wal'); -- 注:参数可以为目录或者文件 |
5. list wal日志文件
1 2 3 | -- 列出wal文件: select walminer_wal_list(); -- 注:参数可以为目录或者文件 |
6. 执行解析
同上
7. 解析结果查看
1 | select * from walminer_contents; |
8.结束walminer操作,该函数作用为释放内存,结束日志分析,该函数没有参数。
1 | select walminer_stop(); |
:warning: 注意:walminer_contents是walminer自动生成的unlogged表(之前是临时表,由于临时表在清理上有问题,引起工具使用不便,所以改为unlogged表),在一次解析开始会首先创建或truncate walminer_contents表。
场景三:自apply解析(开发中的功能,慎用)
场景一和场景二中的解析结果是放到结果表中的,场景三可以将解析结果直接apply到解析数据库中。命令执行的流程与场景一和场景二相同。
1 2 | -- 参数意义参考walminer_by_lsn()接口 select walminer_apply(startlsn, endlsn,'true', reloid); |
此功能可以处理主备切换延迟数据
当主库A发生故障,从库B切换为主库之后。
B库将A库未通过流复制apply的wal日志拷贝到B库可以获取的路径(这一步目前需要DBA自行处理,尚未纳入本功能)
在B库加载wal日志,执行walminer_apply()解析,其中:
startlsn选取未能apply到B库的lsn的开始值
endlsn参数写NULL
'true'这里最好填写‘true’,就不要写‘false’了
reloid是可选参数
walminer_apply()完成后,可以看到延迟的数据已经写到B库了
自apply解析功能说明
- 目前处于coding中,后续会添加严格的txid限制,避免错误修改数据,现在是尝鲜测试版
- 对于有冲突的项目,会把冲突sql存放到
$PGDATA/pg_walminer/wm_analyselog/apply_failure
文件中,供DBA自行判断处理 - 保持事务性,同一个事务中的一条SQLapply失败后,整个事务都会apply失败
- 看大家需求,后续可能考虑增加远程apply功能
场景四:DDL解析
系统表变化解析
目前walminer支持解析系统表的变化。也就是说如果在PG执行了DDL语句,walminer可以分析出DDL语句引起的系统表的变化。
1 2 | -- 在执行解析之前,先执行如下语句,即可开启系统表解析功能 select wal2sql_with_catalog(); |
DDL解析
1 2 | -- 在执行解析之前,先执行如下语句,即可开启DDL解析功能 select wal2sql_with_ddl(); |
:warning:系统表变化解析
和DDL解析
不共存,总是接受最新确定的状态。
:warning:walminer对DML数据的解析是要求没有系统表变化的,因此存在DDL变化时,可能导致DML解析不出来的情况。
使用限制
本版本解析DML语句。DDL语句解析功能正在不断开发。
只能解析与数据字典时间线一致的wal文件
当前walminer无法处理数据字典不一致问题,walminer始终以给定的数据字典为准,
对于无法处理的relfilenode,那么会丢弃这一条wal记录(会有一个notice在解析结果中没有体现)
complete属性只有在wallevel大于minimal时有效
xid解析模式不支持子事务
本人提供Oracle(OCP、OCM)、MySQL(OCP)、PostgreSQL(PGCA、PGCE、PGCM)等数据库的培训和考证业务,私聊QQ646634621或微信db_bao,谢谢!同时只能有一个walminer解析进程,否则会出现解析混乱
使用方法-数据页挽回(坏块修复)
1. 环境搭建
创建extension,创建数据地点,加载wal日志的方法与[SQL解析]中描述的方法一致。
2. 执行数据挽回
1 | select page_collect(relfilenode, reloid, pages) |
relfilenode:需要解析的wal日志中的relfilenode
reloid:解析库中存在的表的OID,此命令将会将从wal中找到的page覆盖到reloid制定的表中
pages:是字符串类型,制定想要挽回的目标page。格式为'0,1,2,7'或者'all'。
具体使用方法可以从pc_base.sql测试用例文件中获取。
此功能持续开发中,后续会添加基于基础备份的数据页挽回
使用限制
1.将部分page恢复到其他表后,查询时可能会出现报错的情况。这是因为恢复后的page可能依赖其他page数据,而其依赖的page没有恢复到这个表中。
2.执行此命令后请立即备份,因为此命令对数据的操作不会记录在wal中。
老版本示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | postgres=# select pg_switch_wal(); pg_switch_wal --------------- 2/AF153088 (1 row) postgres=# create table t_lhr(i int,j int, k varchar); CREATE TABLE postgres=# insert into t_lhr values(1,1,'qqqqqq'); INSERT 0 1 postgres=# postgres=# insert into t_lhr values(2,2,'wwwwww'); INSERT 0 1 postgres=# insert into t_lhr values(3,3,'eeeee'); INSERT 0 1 postgres=# postgres=# update t_lhr set k = '1111qqqqq' where i = 1; UPDATE 1 postgres=# delete from t_lhr where j = 2; DELETE 1 postgres=# postgres=# insert into t_lhr values(4,4,'44444rrrrrr'); INSERT 0 1 postgres=# select pg_walfile_name(pg_current_wal_lsn()); pg_walfile_name -------------------------- 0000000100000002000000B0 (1 row) postgres=# select pg_switch_wal(); pg_switch_wal --------------- 2/B001C1F8 (1 row) postgres=# select pg_walfile_name(pg_current_wal_lsn()); pg_walfile_name -------------------------- 0000000100000002000000B1 (1 row) postgres=# select * from t_lhr; i | j | k ---+---+------------- 3 | 3 | eeeee 1 | 1 | 1111qqqqq 4 | 4 | 44444rrrrrr (3 rows) postgres=# postgres=# select walminer_wal_list(); ERROR: wal list has not been loaded or has been removed. postgres=# postgres=# select count(*) from walminer_contents; count ------- 0 (1 row) postgres=# postgres=# select walminer_wal_add('/pg13/pgdata/pg_wal/0000000100000002000000B0'); walminer_wal_add -------------------- 1 file add success (1 row) postgres=# select walminer_all(); NOTICE: Switch wal to 0000000100000002000000B0 on time 2021-09-27 16:45:17.311174+08 NOTICE: Switch wal to 0000000100000002000000B0 on time 2021-09-27 16:45:17.313633+08 walminer_all --------------------- pg_minerwal success (1 row) postgres=# select count(*) from walminer_contents; count ------- 6 (1 row) postgres=# select * from walminer_contents; sqlno | xid | topxid | sqlkind | minerd | timestamp | op_text | undo_text | complete | schema | relation | start_lsn | commit_lsn -------+--------+--------+---------+--------+-------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------------+----------+--------+----------+------------+------------ 1 | 452478 | 0 | 1 | t | 2021-09-27 16:35:03.326609+08 | INSERT INTO public.t_lhr(i ,j ,k) VALUES(1 ,1 ,'qqqqqq') | DELETE FROM public.t_lhr WHERE i=1 AND j=1 AND k='qqqqqq' | t | public | t_lhr | 2/B001BEB8 | 2/B001BF28 1 | 452479 | 0 | 1 | t | 2021-09-27 16:35:07.045406+08 | INSERT INTO public.t_lhr(i ,j ,k) VALUES(2 ,2 ,'wwwwww') | DELETE FROM public.t_lhr WHERE i=2 AND j=2 AND k='wwwwww' | t | public | t_lhr | 2/B001BF28 | 2/B001BF98 1 | 452480 | 0 | 1 | t | 2021-09-27 16:35:07.84914+08 | INSERT INTO public.t_lhr(i ,j ,k) VALUES(3 ,3 ,'eeeee') | DELETE FROM public.t_lhr WHERE i=3 AND j=3 AND k='eeeee' | t | public | t_lhr | 2/B001BF98 | 2/B001C020 1 | 452481 | 0 | 2 | t | 2021-09-27 16:35:12.005771+08 | UPDATE public.t_lhr SET k='1111qqqqq' WHERE i=1 AND j=1 AND k='qqqqqq' | UPDATE public.t_lhr SET k='qqqqqq' WHERE i=1 AND j=1 AND k='1111qqqqq' | t | public | t_lhr | 2/B001C020 | 2/B001C098 1 | 452482 | 0 | 3 | t | 2021-09-27 16:35:12.775897+08 | DELETE FROM public.t_lhr WHERE i=2 AND j=2 AND k='wwwwww' | INSERT INTO public.t_lhr(i ,j ,k) VALUES(2 ,2 ,'wwwwww') | t | public | t_lhr | 2/B001C098 | 2/B001C0F8 1 | 452483 | 0 | 1 | t | 2021-09-27 16:35:16.778888+08 | INSERT INTO public.t_lhr(i ,j ,k) VALUES(4 ,4 ,'44444rrrrrr') | DELETE FROM public.t_lhr WHERE i=4 AND j=4 AND k='44444rrrrrr' | t | public | t_lhr | 2/B001C130 | 2/B001C1A8 (6 rows) |
WalMiner 4.3版本使用
文档:https://gitee.com/movead/XLogMiner/wikis/walminer4.3/builtdic%E5%8A%9F%E8%83%BD
安装
从下载地址获取最新的发行版
必须以普通用户登录配置:
1 2 3 4 5 6 7 8 9 10 11 | cd /usr/local/ tar -xzvf walminer_x86_64_centos.tar.gz ln -s /usr/local/walminer_x86_64_centos_v4.3.0 /usr/local/walminer cd /usr/local/walminer export PATH=$PATH:/usr/local/walminer/bin/ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/walminer/lib/ -- 覆盖/usr/local/walminer/walminer.license文件,需要购买 walminer help |
执行walminer help
,如果可以正常打印help信息,则证明安装部署成功。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | [lhr@lhrdb walminer]$ walminer help ################################################# Walminer for PostgreSQL wal Persional License for ............... ################################################# walminer [command] [options] COMMANDS --------- #wal2sql options -D dic file for miner -a out detail info for catalog change -w wal file path to miner -t dest of miner result(1 stdout, 2 file, 3 db)(stdout default) -k boundary kind(1 all, 2 lsn, 3 time, 4 xid)(all default) -m miner mode(0 nomal miner, 1 accurate miner)(nomal default) if k=2 -r the relname for single table miner -s start location if k=2 or k=3, or xid if k = 4 if k=2 default the min lsn of input wals if k=3 or k=4 you need input this -e end wal location if k=2 or k=3 if k=2 default the max lsn of input wals if k=3 you need input this -f file to store miner result if t = 2 -d target database name if t=3(default postgres) -h target database host if t=3(default localhost) -p target database port if t=3(default 5432) -u target database user if t=3(default postgres) -W target user password if t=3 --------- #builtdic options -d target database name(default postgres) -h target database host(default localhost) -p target database port(default 5432) -u target database user(default postgres) -W target user password -D dic produce path -f rewrite walminer dic if exists --------- #showdic options -D dic file to show --------- #avatardic options -r avtar rel that new created -n avtared relfilenode -D avtared walminer dic path --------- #regress(not support for user) options -w test database wal path(default postgres) -d test database name(default postgres) -h test database host(default localhost) -p test database port(default 5432) -u test database user(default postgres) -P apply database port -W test user password --------- #fosync options -D dic file for miner -w wal file path to miner -t dest of miner result(1 stdout, 2 file, 3 db, 4 apply)(stdout default) -f file to store miner result if t = 2 -l lsn it start fync -d target database name if t=3 or 4(default postgres) -h target database host if t=3 or 4(default localhost) -p target database port if t=3 or 4(default 5432) -u target database user if t=3 or 4(default postgres) -W target user password if t=3 or 4 --------- #pgto options -c configure path -i to init a CDC configure -r to run a CDC configure Below is needed when -i -d source database name(default postgres) -h source database host(default localhost) -p source database port(default 5432) -u source database user(default postgres) -w source user password -D target database name -H target database host -P target database port -U target database user -W target user password -K target database type(1 postgres) (support postgres only currently) -s slot name need for CDC --------- ################################################# |
使用示例
1 2 3 4 5 6 7 8 9 10 | -- 生成数据字典(需要配置replication连接权限 “host replication all all trust” ) walminer builtdic -D /tmp/walminer.dic -h 172.71.0.18 -p 5432 -d db1 -u postgres -W 'lhr' -f -- 全部解析 walminer wal2sql -D /tmp/walminer.dic -w /var/lib/postgresql/data/pg_wal/ -- 按照时间解析 walminer wal2sql -D /tmp/walminer.dic -w /var/lib/postgresql/data/pg_wal/ -d db1 -W 'lhr' \ -k 3 -s '2023-3-17 10:00:29' -e '2023-3-17 14:08:42' |
builtdic功能
功能综述
walminer解析wal日志时需要从wal日志外的地方获取数据库内部的元数据信息,额外的地方就是指数据库数据数据字典。使用walminer的一些功能需要先从产生wal日志的数据库导出walminer需要的元数据信息,builtdic就是用来导出元数据也就是数据字典的功能模块。
注意目前walminer的解析单位为单库,就是一个PG集簇下的一个数据库。
help信息
1 2 3 4 5 6 7 8 9 | #builtdic options -d target database name(default postgres) -h target database host(default localhost) -p target database port(default 5432) -u target database user(default postgres) -W target user password -D dic produce path -f rewrite walminer dic if exists |
-d指目标数据库名(默认为postgres数据库)
-h指目标数据库IP(默认为localhost)
-p指目标数据库端口(默认为5432)
-u指连接目标数据库的用户(默认为postgres用户)
-W指连接目标数据库的用户密码
-D指生成数据字典的文件(path+name)
-f指如果目标位置已经有文件,那么重写这个文件
使用演示
1 2 3 4 5 6 7 8 | lchch@deepin:data$ walminer builtdic -D /home/lchch/Desktop/walminer.dic -h localhost -p 5432 -u lchch -W 'xxxxxx' -f ################################################# Walminer for PostgreSQL wal Vip License for xingfeng ################################################# DIC INFO# sysid:7159486698322551780 dboid:12664 timeline:1 dbversion:130005 walminer:4.3 lchch@deepin:data$ |
在这个使用示例中,没有指定-d参数那么就默认为postgres数据库生成数据字典。同时打印了下数据字典相关信息和版本号信息。在后面需要使用数据字典的地方就可以指定为/home/lchch/Desktop/walminer.dic
showdic功能
功能综述
查看看数据字典里保存的信息,这个一般不会用到,在开发调试问题时会使用。
help信息
1 2 3 | #showdic options -D dic file to show |
-D指需要查看的数据字典文件
使用演示
如下使用演示,现在结果显示不美观o(╯□╰)o...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | lchch@deepin:data$ walminer showdic -D /home/lchch/Desktop/walminer.dic ################################################# Walminer for PostgreSQL wal Vip License for xingfeng ################################################# DIC INFO# sysid:7159486698322551780 dboid:12664 timeline:1 dbversion:130005 walminer:4.3 User Rel# relname=t1, reloid=16384, relfilenode=16384 relname=t2, reloid=16387, relfilenode=16387 relname=regress_no_pk_table, reloid=24889, relfilenode=24889 relname=regress_test_table_1, reloid=24920, relfilenode=24920 relname=regress_test_table_2, reloid=24928, relfilenode=24928 ......此处省略 |
avatardic功能
功能综述
如果一个表已经被删除,但是用户又想解析wal中这张表的相关操作,正常情况下由于没有历史信息的数据字典,所以无法解析这张表。这里提供了一个修改数据字典的方法,可以重新创建一个与被删除的表表结构完全一致的新表。然后生成数据字典,在数据字典中用新表的relfilenode取代旧表的relfilenode,这样解析结果就能以新表为载体出现。
help信息
1 2 3 4 5 | #avatardic options -r avtar rel that new created -n avtared relfilenode -D avtared walminer dic path |
-r新创建的表的表名
-n已经删除的表的relfilenode
-D被执行修改的数据字典
使用演示
生成数据字典并查看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | lchch@deepin:data$ walminer builtdic -D /home/lchch/Desktop/walminer.dic -d db1 -u lchch -W 123qweQWE -f ################################################# Walminer for PostgreSQL wal Vip License for xingfeng ################################################# DIC INFO# sysid:7159486698322551780 dboid:24952 timeline:1 dbversion:130005 walminer:4.3 lchch@deepin:data$ walminer showdic -D /home/lchch/Desktop/walminer.dic ################################################# Walminer for PostgreSQL wal Vip License for xingfeng ################################################# DIC INFO# sysid:7159486698322551780 dboid:24952 timeline:1 dbversion:130005 walminer:4.3 User Rel# relname=t1, reloid=24953, relfilenode=24953 User Rel Attribute# attname=i, attrelid=24953 User Rel Pkey# lchch@deepin:data$ |
执行avatardic命令
1 2 3 4 5 6 7 | lchch@deepin:data$ walminer avatardic -D /home/lchch/Desktop/walminer.dic -r t1 -n 30000 ################################################# Walminer for PostgreSQL wal Vip License for xingfeng ################################################# Avatar rel t1 to relfilenode 30000 lchch@deepin:data$ |
查看修改后的数据字典
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | lchch@deepin:data$ walminer showdic -D /home/lchch/Desktop/walminer.dic ################################################# Walminer for PostgreSQL wal Vip License for xingfeng ################################################# DIC INFO# sysid:7159486698322551780 dboid:24952 timeline:1 dbversion:130005 walminer:4.3 User Rel# relname=t1, reloid=24953, relfilenode=30000 User Rel Attribute# attname=i, attrelid=24953 User Rel Pkey# lchch@deepin:data$ |
wal2sql功能
功能综述
wal2sql是walminer的核心功能,主要是以各种方式解析wal日志,并得出产生wal的DML语句、其undo语句、事务信息、lsn信息。
help信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #wal2sql options -D dic file for miner -a out detail info for catalog change -C enable DDL miner -w wal file path to miner -t dest of miner result(1 stdout, 2 file, 3 db)(stdout default) -k boundary kind(1 all, 2 lsn, 3 time, 4 xid)(all default) -m miner mode(0 nomal miner, 1 accurate miner)(nomal default) if k=2 -r the relname for single table miner -s start location if k=2 or k=3, or xid if k = 4 if k=2 default the min lsn of input wals if k=3 or k=4 you need input this -e end wal location if k=2 or k=3 if k=2 default the max lsn of input wals if k=3 you need input this -f file to store miner result if t = 2 -d target database name if t=3(default postgres) -h target database host if t=3(default localhost) -p target database port if t=3(default 5432) -u target database user if t=3(default postgres) -W target user password if t=3 |
-D指定解析使用的数据字典
-C开启DDL解析(参照[DDL解析模块]章节)
-w指定解析的wal日志所在的目录
-t指定解析结果的输出方式
1 2 3 | 1 输出到标准输出(默认) 2 输出到-f参数指定的文件 3 保存到一个PG数据库(保存到临时表walminer_contents中) |
-k指定解析类型
1 2 3 4 | 1 解析-w目录中的全部wal日志,无视-s和-e参数(默认) 2 -s和-e参数指定的为解析的开始和结束lsn 3 -s和-e参数指定的为解析的开始和结束时间 4 -s指定的参数为需要解析的事务ID列表 |
-m指定解析类型
1 2 3 4 5 | 0 普通解析(默认) 1 精确解析 k为1时,只能指定为普通解析 精确解析,保证在k不为1时,精确解析出-s和-e指定的范围内的所有数据[参照附录理解]。 |
-r指定当前解析为单表解析,并指定表名
-s当k=2时为开始lsn; 当k=3时为开始时间;当k=4时为xid列表
-e当k=2时为结束lsn; 当k=3时为结束时间;
-f当t为2时,指定文件名
-d 当t=3时指定目标数据库的数据库名(默认postgres)
-h 当t=3时指定目标数据库的地址(默认localhost)
-p 当t=3时指定目标数据库的端口(默认5432)
-u 当t=3时指定目标数据库的连接用户名(默认postgres)
-W 当t=3时指定目标数据库的连接用户的密码
使用演示
首先要生成数据字典,方法参照[builtdic功能]章节
演示全部解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | lchch@deepin:~$ walminer wal2sql -D /home/lchch/Desktop/walminer.dic -w /home/lchch/h2/pg13/data/pg_wal -d db1 -W 123qweQWE ################################################# Walminer for PostgreSQL wal Vip License for xingfeng ################################################# [XID]=794, [TOPXID]=0 [SQLNO]=1 [SQL]=INSERT INTO public.t1(i) VALUES(1) [UNDO]=DELETE FROM public.t1 WHERE i=1 [COMPLETE]=true [LSN]=0/1e06c248 [COMMITLSN]=0/1e06c288 [COMMITTIME]=2022-11-21 14:02:30.851174+00 ------------------------------------------------------ [XID]=795, [TOPXID]=0 [SQLNO]=1 [SQL]=INSERT INTO public.t1(i) VALUES(2) [UNDO]=DELETE FROM public.t1 WHERE i=2 [COMPLETE]=true [LSN]=0/1e06c3d0 [COMMITLSN]=0/1e06c468 [COMMITTIME]=2022-11-21 14:08:41.048496+00 ------------------------------------------------------ lchch@deepin:~$ |
演示lsn解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | lchch@deepin:~$ walminer wal2sql -D /home/lchch/Desktop/walminer.dic -w /home/lchch/h2/pg13/data/pg_wal -d db1 -W 123qweQWE -k 2 -s 0/1e06c247 -e 0/1e06c500 ################################################# Walminer for PostgreSQL wal Vip License for xingfeng ################################################# [XID]=794, [TOPXID]=0 [SQLNO]=1 [SQL]=INSERT INTO public.t1(i) VALUES(1) [UNDO]=DELETE FROM public.t1 WHERE i=1 [COMPLETE]=false [LSN]=0/1e06c248 [COMMITLSN]=0/1e06c288 [COMMITTIME]=2022-11-21 14:02:30.851174+00 ------------------------------------------------------ [XID]=795, [TOPXID]=0 [SQLNO]=1 [SQL]=INSERT INTO public.t1(i) VALUES(2) [UNDO]=DELETE FROM public.t1 WHERE i=2 [COMPLETE]=true [LSN]=0/1e06c3d0 [COMMITLSN]=0/1e06c468 [COMMITTIME]=2022-11-21 14:08:41.048496+00 ------------------------------------------------------ lchch@deepin:~$ |
演示时间解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | lchch@deepin:~$ walminer wal2sql -D /home/lchch/Desktop/walminer.dic -w /home/lchch/h2/pg13/data/pg_wal -d db1 -W 123qweQWE -k 3 -s '2022-11-21 14:02:29' -e '2022-11-21 14:08:42' ################################################# Walminer for PostgreSQL wal Vip License for xingfeng ################################################# Get start lsn 0/1e06bf78 for time range [XID]=794, [TOPXID]=0 [SQLNO]=1 [SQL]=INSERT INTO public.t1(i) VALUES(1) [UNDO]=DELETE FROM public.t1 WHERE i=1 [COMPLETE]=true [LSN]=0/1e06c248 [COMMITLSN]=0/1e06c288 [COMMITTIME]=2022-11-21 14:02:30.851174+00 ------------------------------------------------------ [XID]=795, [TOPXID]=0 [SQLNO]=1 [SQL]=INSERT INTO public.t1(i) VALUES(2) [UNDO]=DELETE FROM public.t1 WHERE i=2 [COMPLETE]=true [LSN]=0/1e06c3d0 [COMMITLSN]=0/1e06c468 [COMMITTIME]=2022-11-21 14:08:41.048496+00 ------------------------------------------------------ lchch@deepin:~$ |
演示事务ID解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | lchch@deepin:~$ walminer wal2sql -D /home/lchch/Desktop/walminer.dic -w /home/lchch/h2/pg13/data/pg_wal -d db1 -W 123qweQWE -k 4 -s 794,795 ################################################# Walminer for PostgreSQL wal Vip License for xingfeng ################################################# Get start lsn 0/1e06c248 for xid range Get end lsn 0/1e06c490 for xid range [XID]=794, [TOPXID]=0 [SQLNO]=1 [SQL]=INSERT INTO public.t1(i) VALUES(1) [UNDO]=DELETE FROM public.t1 WHERE i=1 [COMPLETE]=false [LSN]=0/1e06c248 [COMMITLSN]=0/1e06c288 [COMMITTIME]=2022-11-21 14:02:30.851174+00 ------------------------------------------------------ [XID]=795, [TOPXID]=0 [SQLNO]=1 [SQL]=INSERT INTO public.t1(i) VALUES(2) [UNDO]=DELETE FROM public.t1 WHERE i=2 [COMPLETE]=true [LSN]=0/1e06c3d0 [COMMITLSN]=0/1e06c468 [COMMITTIME]=2022-11-21 14:08:41.048496+00 ------------------------------------------------------ lchch@deepin:~$ |
附录
解析原理
walminer在解析一个wal记录时,可能需要依赖之前的wal记录,原因可以参照博客中的[不同Wal级别记录DML语句的方式]章节。根据wal的FPW特性(检查点开始后首次对一个数据页进行修改时,需要在wal中记录这个数据也的全部信息),walminer可以做到,一个检查点的redo点之后的所有信息可以完全被解析出来。
精确解析时通过-s和-e指定了解析范围,解析范围能确定lsn范围,在这些lsn范围之前在找到一个checkpoin点的redo点就可解析范围内的所有数据。
即使精确解析,也存在解析范围内的事务解析不全的现象,比方指定了范围lsn1~lsn2,可能一个事务开始的lsn比lsn1小,结束的lsn比lsn1大,那么这个事务比lsn1小的部分就无法解析出来。可以通过解析结果的complete字段识别这种情况。
版本更新
walminer4.3
添加-C参数,用于支持DDL解析
添加-a参数,用于支持catalog变更输出
解析过程遇到数据字典变更时,把变更同步到现有数据字典信息
fosync功能
导出
功能综述
PG主备在异步流复制的情况下,备库LSN可能比主库LSN小很多,这其中可能有一部分wal在备库还没有redo,也有可能还有一部分在主库还没有发送到备库。第一种情况不用担心,备库提升主库时会处理这些数据;但是在第二这种情况下,现有方案无法处理这部分没有发送到备库的延迟数据。
fosync是在PG流复制发生故障转移后,将主备延迟部分的数据同步到备库的方案。
功能原理
PG备库提升主库后,会在pg_wal目录下的xxxx.history文件中记录,提升主库的lsn位点。也就是这个lsn之前的事务提交已经在新主库中可见,这个lsn之后的提交的数据,在新主库这种没有或不可见。walminer fosync功能就是通过解析主库wal日志,把lsn位点之后提交的数据解析出来,重新通过SQL的形式apply到新主库中。
help信息
1 2 3 4 5 6 7 8 9 10 11 12 | #fosync options -D dic file for miner -w wal file path to miner -t dest of miner result(1 stdout, 2 file, 3 db, 4 apply)(stdout default) -f file to store miner result if t = 2 -l lsn it start fync -d target database name if t=3 or 4(default postgres) -h target database host if t=3 or 4(default localhost) -p target database port if t=3 or 4(default 5432) -u target database user if t=3 or 4(default postgres) -W target user password if t=3 or 4 |
-D数据字典文件,可以从原主库或新主库获取
-w指定需要解析的wal文件所在的目录
-t解析结果的输出位置
1 2 3 4 | 1 标准输出 2 输出到文件 3 输出到一个PG数据库,可供搜索 4 apply到新主库 |
-f如果t为2那么在这个参数指定输出文件
-l备库提升为主库的lsn位点
-d当t为3或4时,指定目标数据库名
-h当t为3或4时,指定目标数据库IP
-p当t为3或4时,指定目标数据库端口
-u当t为3或4时,指定目标数据库连接用户
-W当t为3或4时,指定目标数据库连接用户密码
版本更新
walminer4.3
解析过程遇到数据字典变更时,把变更同步到现有数据字典信息
解析DDL结果
DDL解析模块
功能综述
数据库中有太多的DDL命令,个人力量难以完成所有的DDL命令的解析。目前实现了对几个重要的DDL命令的解析。
目前DDL解析只实现在了wal2sql功能中,详情参照wal2sql的-C参数。
已经实现的DDL命令
CREATE TABLE
DROP TABLE
TRUNCATE TABLE
RENAME TABLE
ALTER TABLE...ADD COLUMN
ALTER TABLE...DROP COLUMN
ALTER TABLE...ALTER COLUMN TYPE
ALTER TABLE...RENAME COLUMN
适用范围
目前DDL解析只实现在了wal2sql功能中
dic同步模块
导出
模块综述
-------------D1--------C1---------->lsn
在给定的WAL中,如果发生了数据结构的变更,比如在D1点创建了数据字典,解析过程使用到了C1点的WAL日志,而在C1点创建了新的表。在这种情况下,由于在数据字典中没有新表的数据结构,所以无法解析。dic同步模块就是在遇到数据字典变更的wal时,将变更同步到内存中的数据字典中,以供解析使用。
对于D1点之前(创建数据字典之前)的数据结构变更不会做额外处理。
数据字典同步
在解析过程中,如果遇到数据字典变更,则将变更信息同步到内存中的数据字典中。
注意:
1.只有大于数据字典创建的wal,才会引起数据字典同步
2.解析完成后,数据字典不会同步到磁盘上的数据字典
3.即使一些行为不会被DDL解析出来,他也会完成数据字典同步动作
版本限制
如果一个事务中做了表结构的变更或者表的创建,这个事务中也进行了这个表的DML操作,那么这个事务中的DML操作解析可能会出问题。
总结
1、WalMiner 4.0版本更好,但是收费,请参考:https://gitee.com/movead/XLogMiner/wikis/walminer%20license
2、WalMiner 4.0可以wal日志拷贝出来,然后远程生成字典,进行远程解析。
参考
https://gitee.com/movead/XLogMiner/wikis/walminer4.3/wal2sql%E5%8A%9F%E8%83%BD