ud_dirtyread

简介

ud_dirtyread 是脏读插件,用于读取那些已经删除但未被清理的数据。当误操作delete数据时,可以使用它从脏数据中恢复出来。 不支持 DDL 操作和 truncate 操作的恢复。

前提要求

  • 未手动或自动执行 vacuum 清理操作

查询上一次 vacuum 执行时间,如已经执行了清理,则数据无法恢复。

select relname,last_vacuum, last_autovacuum, last_analyze, vacuum_count, autovacuum_count,  last_autoanalyze from pg_stat_user_tables;

注意事项

  • 不建议在生产环境直接执行本节的所有操作。

  • 建议将当前所有文件复制到一个独立的机器进行恢复操作,避免对当前环境造成更大的数据损坏。

  • 在将结果恢复到生产环境时,建议先备份生产环境当前的所有数据。

安装

udb-tx-v22.4.22及以后的版本,已启用 ud_dirtyread, 直接使用即可。 udb-tx-v22.4.22及以前的版本,按以下步骤安装启用。

【附件】ud_dirtyread.zip 如下载失败请联系技术支持获取。

启用

  • ud_dirtyread.so 放到安装目录的lib/ 下

  • ud_dirtyread.control 和 ud_dirtyread–*.sql 放到安装目录的share/extension/ 下

  • chown -R udb.udb 安装目录/lib/ud_dirtyread.so

  • chown -R udb.udb 安装目录/share/extension/ud_dirtyread*

验证安装

waltest=#  CREATE EXTENSION ud_dirtyread;
CREATE EXTENSION

使用示例

创建测试数据

waltest=# select * from tb1;
 id | name 
----+------
  1 | a
  2 | b
(2 rows)
waltest=# \dt tb1;
       List of relations
 Schema | Name | Type  | Owner 
--------+------+-------+-------
 public | tb1  | table | unvdb
(1 row)
waltest=# \d tb1;
                        Table "public.tb1"
 Column |          Type          | Collation | Nullable | Default 
--------+------------------------+-----------+----------+---------
 id     | integer                |           | not null | 
 name   | character varying(255) |           |          | 
Indexes:
    "tb1_pkey" PRIMARY KEY, btree (id)

查看当前数据元组信息

waltest=# select xmin,xmax,ctid,* from tb1;     
 xmin | xmax | ctid  | id | name 
------+------+-------+----+------
 1650 |    0 | (0,1) |  1 | a
 1651 |    0 | (0,2) |  2 | b
(2 rows)

查看是否为脏数据,dead列为f,表示非死数据

waltest=# select * from ud_dirtyread('tb1') as foo(tableoid oid, ctid tid, xmin xid, xmax xid, cmin cid, cmax cid, dead boolean,id int,name varchar(255)); 
 tableoid | ctid  | xmin | xmax | cmin | cmax | dead | id | name 
----------+-------+------+------+------+------+------+----+------
    66027 | (0,1) | 1650 |    0 |    0 |    0 | f    |  1 | a
    66027 | (0,2) | 1651 |    0 |    0 |    0 | f    |  2 | b
(2 rows)

删除数据

waltest=# delete from tb1 where id=1;
DELETE 1
waltest=# select * from tb1;
 id | name 
----+------
  2 | b
(1 row)

修改数据

waltest=# update tb1 set name='bb' where id=2;
UPDATE 1

查看已删除未清理的脏数据

waltest=# select * from ud_dirtyread('tb1') as foo(tableoid oid, ctid tid, xmin xid, xmax xid, cmin cid, cmax cid, dead boolean,id int,name varchar(255));
 tableoid | ctid  | xmin | xmax | cmin | cmax | dead | id | name 
----------+-------+------+------+------+------+------+----+------
    66027 | (0,1) | 1650 | 1652 |    0 |    0 | t    |  1 | a
    66027 | (0,2) | 1651 | 1653 |    0 |    0 | t    |  2 | b
    66027 | (0,3) | 1653 |    0 |    0 |    0 | f    |  2 | bb
(3 rows)

会发现id=1的那条数据状态 dead=t 更新的id=2那条数据的事务已经由1651变为1653,原来的状态变为了删除,值已经变更为bb

删除列

waltest=# alter table tb1 drop column name;
ALTER TABLE
waltest=# select * from tb1;
 id 
----
  2
(1 row)

查看已删除列的脏数据,此时要用 dropped_n 来代替

waltest=# select * from ud_dirtyread('tb1') as foo(tableoid oid, ctid tid, xmin xid, xmax xid, cmin cid, cmax cid, dead boolean,id int,dropped_2 varchar(255)); 
 tableoid | ctid  | xmin | xmax | cmin | cmax | dead | id | dropped_2 
----------+-------+------+------+------+------+------+----+-----------
    66027 | (0,1) | 1650 | 1652 |    0 |    0 | t    |  1 | a
    66027 | (0,2) | 1651 | 1653 |    0 |    0 | t    |  2 | b
    66027 | (0,3) | 1653 |    0 |    0 |    0 | f    |  2 | bb
(3 rows)