Walminer2.0 Beta功能改进说明
walminer是一款PostgreSQL的wal日志的解析工具,它可以依据数据字典解析出产生wal日志中隐含的DML语句。第一个版本的xlogminer工具需要wal为logical级别,而且还有表IDENTITY级别的要求。第二个版本的walminer在易用性上不太友好,无法做到精准解析。walminer2.0是第三个版本,这个版本在上一个版本的基础上增强了易用性,可以做到精准解析,并且做了大量的代码重构以期获得更好的扩展性和对PG13的支持。
项目开源地址为:https://gitee.com/movead/XLogMiner, 有兴趣的同学可以下载测试,目前walminer2.0还处于开发过程,如果大家有好的改进意见可以提issue,顺便记得star~~~
功能增强
- 支持指定LSN范围解析
- 支持特定事务ID解析
- 支持精确解析语法
- 增加快捷解析和化身解析模式
版本支持
支持PG9.5,PG9.6,PG10,PG11,PG12,PG13,与上一个版本不同,walminer2.0的代码完成了合并,使用同一份walminer代码可以对应各个版本的PG的编译安装。
编译安装
编译一:从PG源码编译
-
将walminer目录放置到编译通过的PG工程的"../contrib/"目录下
-
进入walminer目录
-
执行命令
make && make install
编译二:依据安装PG的编译
-
配置pg的bin路径至环境变量
export PATH=/h2/pg_install/bin:$PATH
-
进入walminer代码路径
-
执行编译安装
USE_PGXS=1 MAJORVERSION=12 make #MAJORVERSION支持‘9.5’,‘9.6’,‘10’,‘11’,‘12’,‘13’ USE_PGXS=1 MAJORVERSION=12 make install
walminer工具使用的标准步骤
walminer2.0与之前的版本一样,都支持在产生wal日志的数据库(生产库)或者在另外的数据库(测试库)执行walminer解析操作。所不同的是,在测试库解析过程需要首先在生产库生成数据字典,然后拿到测试库执行导入数据字典的操作。本博客主要讲述walminer2.0更新的使用接口,用生产库直接解析的方式进行说明,需要在测试库解析的同学可以去阅读下说明文档。
1. 编译安装
略...(不会的可以阅读说明文档或者邮件咨询我)
2. 创建extension
postgres=# create extension walminer; CREATE EXTENSION postgres=#
3. 清理walminer内存空间
postgres=# select walminer_stop(); walminer_stop ------------------ walminer stoped! (1 row) postgres=#
为什么清理空间放在了最前面呢,如果在同一个session中执行多次解析,且有一次执行失败,有些数据可能会遗留在内存中,影响下一次的解析。因此如果你某一次执行解析失败,最好执行一下这个接口,清理一下遗留的内存数据。
4. 管理要解析的wal日志
添加wal日志段
postgres=# select walminer_wal_add('pg_wal'); walminer_wal_add --------------------- 31 file add success (1 row) -- 或者 postgres=# select walminer_wal_add('/h2/pg_walminer_12/data/pg_wal'); walminer_wal_add --------------------- 31 file add success (1 row) -- 或者 postgres=# select walminer_wal_add('/h2/pg_walminer_12/data/pg_wal/000000010000000000000072'); walminer_wal_add -------------------- 1 file add success (1 row)
查看添加的wal日志段
postgres=# select walminer_wal_list(); walminer_wal_list ----------------------------------------------------------- (/h2/pg_walminer_12/data/pg_wal/000000010000000000000072) (/h2/pg_walminer_12/data/pg_wal/000000010000000000000073) (/h2/pg_walminer_12/data/pg_wal/000000010000000000000074) (/h2/pg_walminer_12/data/pg_wal/000000010000000000000075) (/h2/pg_walminer_12/data/pg_wal/000000010000000000000076) (/h2/pg_walminer_12/data/pg_wal/000000010000000000000077) (/h2/pg_walminer_12/data/pg_wal/000000010000000000000078) (/h2/pg_walminer_12/data/pg_wal/000000010000000000000079) (/h2/pg_walminer_12/data/pg_wal/00000001000000000000007A) ... (31 rows) postgres=#
移除wal段
postgres=# select walminer_wal_remove('/h2/pg_walminer_12/data/pg_wal/00000001000000000000007A'); walminer_wal_remove ----------------------- 1 file remove success (1 row)
目前仅支持一个文件一个文件的移除。
5. 执行解析
postgres=# select wal2sql(); NOTICE: Switch wal to 000000010000000000000074 on time 2020-06-17 21:29:08.289991+08 walminer_all --------------------- pg_minerwal success (1 row) postgres=#
walminer2.0简化了解析函数的入参,同时也修改了解析函数的函数名。这里wal2sql()只是最简单的解析方式,还有更多的解析模式将会在下面给出。
6. 查询解析结果
postgres=# select * from walminer_contents; -- 这里不显示结果了 postgres=# \d walminer_contents Table "pg_temp_3.walminer_contents" Column | Type | Collation | Nullable | Default -----------+--------------------------+-----------+----------+--------- sqlno | integer | | | xid | bigint | | | topxid | bigint | | | sqlkind | integer | | | minerd | boolean | | | timestamp | timestamp with time zone | | | op_text | text | | | undo_text | text | | | complete | boolean | | | /* 表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所在的事务是不完整解析的 )*/
walminer2.0的解析模式
知识储备
因为在下面的说明中会提到relfilenode,这里补充一点关于表的oid和relfilenode的说明:
postgres=# select oid, relfilenode,relname from pg_class where relname ='t2'; oid | relfilenode | relname -------+-------------+--------- 90506 | 90506 | t2 (1 row) postgres=# truncate t2; TRUNCATE TABLE postgres=# select oid, relfilenode,relname from pg_class where relname ='t2'; oid | relfilenode | relname -------+-------------+--------- 90506 | 98623 | t2 (1 row) postgres=#
oid是表的唯一标识,relfilenode是表数据存放在硬盘的文件名的标识,在执行vacuum full;truncate等操作时,会导致表在硬盘上存储的文件名的改变,因此relfilenode也会改变。上面这个例子中,新建t2表时t2的oid和relfilenode是相同的,经过一次truncate后,relfilenode发生了改变。好了下面进行walminer2.0的功能讲述。
普通解析
walminer2.0支持如下解析方式:
-- 全部解析 select walminer_all(); 或 select wal2sql(); -- 时间范围解析 select walminer_by_time(starttime, endtime); 或 select wal2sql(starttime, endtime); -- lsn范围解析 select walminer_by_lsn(startlsn, endlsn); 或 select wal2sql(startlsn, endlsn); -- xid解析(注:只支持指定xid,不支持xid范围,目前不支持子事务。) select walminer_by_xid(xid); 或 select wal2sql(xid);
实际操作
postgres=# create table t1(i int, j int, k varchar); CREATE TABLE postgres=# select pg_current_wal_lsn(); pg_current_wal_lsn -------------------- 0/1645F10 (1 row) postgres=# select now(); now ------------------------------- 2020-06-17 22:46:07.992138+08 (1 row) postgres=# select txid_current(); txid_current -------------- 499 (1 row) postgres=# insert into t1 values(1,1,'test_walminer2.0'); INSERT 0 1 postgres=# insert into t1 values(2,1,'support_walminer2.0'); INSERT 0 1 postgres=# select pg_current_wal_lsn(); pg_current_wal_lsn -------------------- 0/1646080 (1 row) postgres=# select now(); now ------------------------------- 2020-06-17 22:46:08.025568+08 (1 row) postgres=# select txid_current(); txid_current -------------- 502 (1 row) postgres=#
postgres=# select walminer_stop(); walminer_stop ------------------ walminer stoped! (1 row) postgres=# select walminer_wal_add('pg_wal'); walminer_wal_add -------------------- 1 file add success (1 row) postgres=# select walminer_by_time('2020-06-17 22:46:07.992138+08','2020-06-17 22:46:08.025568+08'); NOTICE: Switch wal to 000000010000000000000001 on time 2020-06-17 22:48:30.429331+08 walminer_by_time --------------------- pg_minerwal success (1 row) postgres=# \x Expanded display is on. postgres=# select * from walminer_contents; -- 这里我们不显示结果了 postgres=#
快捷解析
加载wal日志步骤可以省略,默认直接加载当前wal路径下的所有wal文件。这个解析模式只在学习本工具时使用,在生产数据库中,可能会因为wal段切换而导致解析失败。
依旧使用上面的数据做解析测试:
postgres=# select walminer_stop(); walminer_stop ------------------ walminer stoped! (1 row) postgres=# select wal2sql('0/1645F10'::pg_lsn,'0/1646080'::pg_lsn); NOTICE: Add wal from current pg_wal directory, do not suggest use this way in produce NOTICE: Switch wal to 000000010000000000000001 on time 2020-06-17 22:53:52.470086+08 wal2sql --------------------- pg_minerwal success (1 row) postgres=# select * from walminer_contents; -- 这里我们不显示结果了
精确解析
walminer的解析理论基础是在checkpoint之后对每一个数据页的第一次有意义的修改都要进行FPW,因此理论上只要找到了checkpoint点的开始点,那么walminer可以解析开始点(如下图的lsn1点)之后的所有记录(当然是在数据字典匹配的情况下)。如果用户想完全解析lsn2到lsn3之间的记录,可以使用精确解析模式,walminer会在加载的wal日志中查找lsn2之前的lsn1点,如果找到lsn1点那么此次精确解析可以完成,如果找不到lsn1点会报错停止解析,并要求用户添加更多wal日志。
精确解析模式支持时间范围解析、lsn范围解析和xid解析,如下解析语法,在普通解析模式下增加一个‘true’参数:
-- 时间范围解析 select walminer_by_time(starttime, endtime,'true'); 或 select wal2sql(starttime, endtime,'true'); -- lsn范围解析 select walminer_by_lsn(startlsn, endlsn,'true'); 或 select wal2sql(startlsn, endlsn,'true'); -- xid解析 select walminer_by_xid(xid,'true'); 或 select wal2sql(xid,'true');
操作也很简单,这里也不再演示。
单表解析
只对指定表的解析,如下语法:
'true'和‘false’代表是否为精确解析模式,reloid为目标表的oid(注意不是relfilenode)
--在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);
操作也很简单,这里也不再演示。
化身解析
如果一个表被drop或者被truncate等操作,导致新产生的数据字典不包含旧的数据库中所包含的relfilenode,那么使用新的数据字典无法解析出旧的wal日志中包含的的某些内容。在知晓旧表的表结构的前提下,可以使用化身解析模式。
-- 假设表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表的形式展现在输出结果中
实际操作
-- 测试数据准备 postgres=# select pg_current_wal_lsn(); pg_current_wal_lsn -------------------- 0/520D578 (1 row) postgres=# insert into t1 values(1,1,'test_walminer'); INSERT 0 1 postgres=# insert into t1 values(2,1,'support_walminer'); INSERT 0 1 postgres=# select relfilenode from pg_class where relname ='t1'; relfilenode ------------- 49275 (1 row) -- 这里执行了truncate导致t1表的relfilenode由49275变为49281 postgres=# truncate t1; TRUNCATE TABLE postgres=# select relfilenode from pg_class where relname ='t1'; relfilenode ------------- 49281 (1 row) postgres=# insert into t1 values(3,1,'after truncate'); INSERT 0 1 postgres=# insert into t1 values(4,1,'after truncate'); INSERT 0 1 postgres=# select pg_current_wal_lsn(); pg_current_wal_lsn -------------------- 0/52123B0 (1 row) postgres=#
-- 使用普通解析模式进行解析 postgres=# select wal2sql('0/520D578'::pg_lsn,'0/52123B0'::pg_lsn); NOTICE: Add wal from current pg_wal directory, do not suggest use this way in produce NOTICE: Switch wal to 000000010000000000000005 on time 2020-06-18 11:16:06.383658+08 -- 这里会通知,49275在数据字典里没有找到 NOTICE: Con not find relfilenode 49275 in dictionary, ignored related records wal2sql --------------------- pg_minerwal success (1 row) postgres=# \x Expanded display is on. postgres=# select * from walminer_contents; -[ RECORD 1 ]------------------------------------------------------------- sqlno | 1 xid | 268435484 topxid | 0 sqlkind | 1 minerd | t timestamp | 2020-06-18 11:14:45.380952+08 op_text | INSERT INTO public.t1(i ,j ,k) VALUES(3 ,1 ,'after truncate') undo_text | DELETE FROM public.t1 WHERE i=3 AND j=1 AND k='after truncate' complete | t -[ RECORD 2 ]------------------------------------------------------------- sqlno | 1 xid | 268435485 topxid | 0 sqlkind | 1 minerd | t timestamp | 2020-06-18 11:14:51.628774+08 op_text | INSERT INTO public.t1(i ,j ,k) VALUES(4 ,1 ,'after truncate') undo_text | DELETE FROM public.t1 WHERE i=4 AND j=1 AND k='after truncate' complete | t -- 解析结果中也没有前面两条数据,如果我们想要解析出丢失的数据,可以使用化身解析模式 postgres=#
-- 演示化身解析模式 postgres=# select walminer_stop(); walminer_stop ------------------ walminer stoped! (1 row) -- 创建化身表 postgres=# create table t1_avatar(i int,j int, k varchar); CREATE TABLE -- 创建丢失的49275与化身表的映射 postgres=# select walminer_table_avatar('t1_avatar', 49275); walminer_table_avatar ----------------------------- MAP[t1_avatar:49296]->49275 (1 row) -- 执行解析 postgres=# select wal2sql('0/520D578'::pg_lsn,'0/52123B0'::pg_lsn); NOTICE: Add wal from current pg_wal directory, do not suggest use this way in produce NOTICE: Switch wal to 000000010000000000000005 on time 2020-06-18 11:19:36.161709+08 wal2sql --------------------- pg_minerwal success (1 row) -- 查询解析结果,可以看到丢失的数据以t1_avatar表的形式,重新体现出来了 postgres=# \x Expanded display is on. postgres=# select * from walminer_contents; -[ RECORD 1 ]---------------------------------------------------------------------- sqlno | 1 xid | 268435481 topxid | 0 sqlkind | 1 minerd | t timestamp | 2020-06-18 11:13:40.168623+08 op_text | INSERT INTO public.t1_avatar(i ,j ,k) VALUES(1 ,1 ,'test_walminer') undo_text | DELETE FROM public.t1_avatar WHERE i=1 AND j=1 AND k='test_walminer' complete | t -[ RECORD 2 ]---------------------------------------------------------------------- sqlno | 1 xid | 268435482 topxid | 0 sqlkind | 1 minerd | t timestamp | 2020-06-18 11:13:48.141368+08 op_text | INSERT INTO public.t1_avatar(i ,j ,k) VALUES(2 ,1 ,'support_walminer') undo_text | DELETE FROM public.t1_avatar WHERE i=2 AND j=1 AND k='support_walminer' complete | t -[ RECORD 3 ]---------------------------------------------------------------------- sqlno | 1 xid | 268435484 topxid | 0 sqlkind | 1 minerd | t timestamp | 2020-06-18 11:14:45.380952+08 op_text | INSERT INTO public.t1(i ,j ,k) VALUES(3 ,1 ,'after truncate') undo_text | DELETE FROM public.t1 WHERE i=3 AND j=1 AND k='after truncate' complete | t -[ RECORD 4 ]---------------------------------------------------------------------- sqlno | 1 xid | 268435485 topxid | 0 sqlkind | 1 minerd | t timestamp | 2020-06-18 11:14:51.628774+08 op_text | INSERT INTO public.t1(i ,j ,k) VALUES(4 ,1 ,'after truncate') undo_text | DELETE FROM public.t1 WHERE i=4 AND j=1 AND k='after truncate' complete | t postgres=#
另外在化身解析模式中,不检查数据字典与wal日志是否匹配的问题,也就是说......
拿到一个wal日志之后,如果你知道某一个relfilenode对应的表的表结构,那么就可以在不需要数据字典的情况下,把这儿wal日志拿到另外的数据库做化身解析,有兴趣的同学可以尝试一下...
化身解析一定要注意表结构的匹配问题,否则极有可能会产生意外的崩溃(暂时还没有找到规避崩溃的方法)
walminer2.0的限制
-
当前walminer无法处理数据字典不一致问题,walminer始终以给定的数据字典为准,对于无法处理的relfilenode,那么会丢弃这一条wal记录(会有一个notice出现)
-
complete属性只有在wallevel大于minimal时有效
-
xid解析模式不支持子事务
-
同时只能有一个walminer解析进程,否则会出现解析混乱
-
化身表不支持toast,化身表与原表结构不一致时会产生崩溃
联系我
发现bug或者有好的建议可以通过邮箱(lchch1990@sina.cn)联系我。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
2G/3G退网及用户向4G迁移
最近,Strategy Analytics基于其免费报告《低成本4G手机:市场动态和机遇》,并结合最新的产业发展情况,分析了当前2G/3G的退网趋势和4G用户迁移机会。 4G主导了当前的移动通信市场 根据Strategy Analytics最新的无线市场预测,今年初,2G和3G用户占全球移动用户总数的46%,但贡献的收入却只占全球移动总收入的27%。到2023年,这一收入占比将下降到10%。非洲可以认为是一个特例,在这个地区,各国的ARPU值低于2美元,因此通过补贴促进用户迁移可操作空间有限。 然而,非洲和其他发展中地区在促进4G业务方面也取得了不错的进展,例如,非洲Airtel 的4G网络扩容和“大套餐包”推动了用户向4G迁移,在数据平均使用量和数据ARPU值方面都得到大幅提升,过去的一年有3/4的收入增长来自于数据,2020年3月,4G在数据用户中占比从去年的18%上升到29%,4G收入在Airtel数据收入占比中超过60%。 2G/3G退网进程紧锣密鼓 类似于Airtel,运营商驱动用户向4G迁移的主要动力包括:提升收入,提升频谱效率,减少由于多网运营带来的OPEX CAPEX支...
- 下一篇
效率思维模式与Zombie Scrum
Scrum是由Ken Schwaber和Jeff Sutherland在20世纪90年代提出的概念,并在1995年首次正式确定。起初Scrum是为了解决产品和软件开发固有的复杂性,然而现在Scrum被成功地应用于市场营销、组织变革和科学研究等多个领域的复杂问题。 Scrum主要建立在以下三个原则的基础上: 透明度:你需要收集数据(比如一些指标、团队成员的反馈或其他团队的经验之谈),从而找到你的目标。 检查:你需要和大家一起监督迭代的进度,并决定迭代完成的标准是什么。 适应:你需要做出改变,希望能更好更快地完成你的目标。 在实施Scrum之前首先要用一段时间来定义和调整这些规则,以发现工作中的问题,找到可以改善的方向,这里说的问题不是那种一年一次或项目完成时才发生的问题,而是每天、每周或每月都在持续发生的问题。我们不是将我们的决策建立在对可能永远不会发生的潜在风险的假设上,而是根据我们收集到的数据来做决策,这就是所谓的经验主义。 Scrum的价值? 当你需要接受你并不了解和无法控制一切的时候,Scrum提供的经验方法就会变得非常有用。也正因如此,你会改变之前的想法,虽然可能会犯错,但也会...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8编译安装MySQL8.0.19
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS关闭SELinux安全模块
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Red5直播服务器,属于Java语言的直播服务器