本文已参与「新人创作礼」活动,一起开启掘金创作之路
刚写了篇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的结构,千万不要随便再生产环境使用!