PostgreSQL特殊恢复——直接修改数据文件恢复数据

262 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路


刚写了篇PostgreSQL的page结构的文章,算是把page弄得比较透彻了。

那么知道这些对我们有啥用呢,下面我们演示下类似Oracle bbed通过修改数据文件来恢复数据的方式。

创建测试表并插入数据:

bill=# create table t1(id int,info text);
CREATE TABLE
bill=# insert into t1 select generate_series(1,5),'bill';
INSERT 0 5
bill=# select * from t1;
 id | info
----+------
  1 | bill
  2 | bill
  3 | bill
  4 | bill
  5 | bill
(5 rows)

查看page结构:

bill=# select * from heap_page_items(get_raw_page('t1',0));
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid |        t_data
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------+----------------------
  1 |   8152 |        1 |     33 |    506 |      0 |        0 | (0,1)  |           2 |       2306 |     24 |        |       | \x010000000b62696c6c
  2 |   8112 |        1 |     33 |    506 |      0 |        0 | (0,2)  |           2 |       2306 |     24 |        |       | \x020000000b62696c6c
  3 |   8072 |        1 |     33 |    506 |      0 |        0 | (0,3)  |           2 |       2306 |     24 |        |       | \x030000000b62696c6c
  4 |   8032 |        1 |     33 |    506 |      0 |        0 | (0,4)  |           2 |       2306 |     24 |        |       | \x040000000b62696c6c
  5 |   7992 |        1 |     33 |    506 |      0 |        0 | (0,5)  |           2 |       2306 |     24 |        |       | \x050000000b62696c6c
(5 rows)

接下来我们删除一条数据,来试试看能不能恢复:

bill=# delete from t1 where id = 1;
DELETE 1
bill=# select * from heap_page_items(get_raw_page('t1',0));
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid |        t_data
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------+----------------------
  1 |   8152 |        1 |     33 |    506 |    507 |        0 | (0,1)  |        8194 |        258 |     24 |        |       | \x010000000b62696c6c
  2 |   8112 |        1 |     33 |    506 |      0 |        0 | (0,2)  |           2 |       2306 |     24 |        |       | \x020000000b62696c6c
  3 |   8072 |        1 |     33 |    506 |      0 |        0 | (0,3)  |           2 |       2306 |     24 |        |       | \x030000000b62696c6c
  4 |   8032 |        1 |     33 |    506 |      0 |        0 | (0,4)  |           2 |       2306 |     24 |        |       | \x040000000b62696c6c
  5 |   7992 |        1 |     33 |    506 |      0 |        0 | (0,5)  |           2 |       2306 |     24 |        |       | \x050000000b62696c6c
(5 rows)

可以看到第一条数据的t_xmax,t_infomask2和t_infomask都发生了变化,至于这些字段是啥意思可以看看我之前的文章。

那么方法就很简单了,只要将文件中这三个字段修改就可以了。

查看表对应数据文件:

bill=# select pg_relation_filepath('t1');
 pg_relation_filepath
----------------------
 base/16385/16450
(1 row)

接下来我们将库停掉,然后再去修改文件。操作前记得备份!

vi打开文件,通过vi的**:%!xxd**命令将文件转为十六进制的:
在这里插入图片描述
接下来我们去修改第一行记录的这三个地方,首先我们要确定在哪。

通过前面pageinspect我们可以知道第一条记录的偏移量lp_off是8152,那么xmin为4个字节,是8153 ~ 8156,xmax则是8157~8160,可以看到0x1fe0就是十进制的8160,那么上面一行尾部的fb01就是xmax。

因为linux是小字节序的,这意味着实际存的数是反过来的,转换下就是01fb,换成十进制刚好是507,前面查出的xmax值。我们这里把它修改成0,即0000。

接下来我们只需要依次将t_infomask2和t_infomask修改就可以了:
在这里插入图片描述
最后将其保存退出然后再重启数据库即可。

再次查看,t1被删除的数据恢复回来了!

bill=# select * from t1;
 id | info
----+------
  1 | bill
  2 | bill
  3 | bill
  4 | bill
  5 | bill
(5 rows)

最后需要说明下,如果数据库开启了checksum这种方法是不行的。
当然这只是为了测试,帮助我们更好的理解page的结构,千万不要随便再生产环境使用!