VastbaseG100

基于openGauss内核开发的企业级关系型数据库。

Menu

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类型
  • 1:INSERT
  • 2:UPDATE
  • 3:DELETE
  • 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)