Use pg_resetxlog simulate tuple disappear within PostgreSQL

3 minute read

背景

刚才francs问我由于PostgreSQL的MVCC机制,是不是数据库维护会导致数据库的记录消失。

当然不会。

PostgreSQL是有保护机制的,有兴趣的朋友可以参考数据库的maintenance章节和相关的freeze参数。

不过有一种情况,可以模拟记录”消失”的情况,那就是使用pg_resetxlog去修改控制文件的NextXID的值。(警告,一般情况下请勿使用)

postgres@db5-> psql -h 127.0.0.1 digoal digoal  

创建测试表

digoal=> create table resetxlog_test (id int);  
CREATE TABLE  
digoal=> insert into resetxlog_test values (1);  
INSERT 0 1  
digoal=> insert into resetxlog_test values (2);  
INSERT 0 1  
digoal=> insert into resetxlog_test values (3);  
INSERT 0 1  
digoal=> insert into resetxlog_test values (4);  
INSERT 0 1  
digoal=> insert into resetxlog_test values (5);  
INSERT 0 1  
digoal=> insert into resetxlog_test values (6);  
INSERT 0 1  

插入6条测试数据后查看记录的xmin和当前的txid.注意我们现在的PostgreSQL epoch是0 (可以从pg_controldata的输出结果看出来) ,所以现在txid和xid相等。

注意PostgreSQL 的epoch和 UNIX的epoch (from 1970)不要混淆。

digoal=> select id,xmin,txid_current() from resetxlog_test ;  
 id | xmin | txid_current   
----+------+--------------  
  1 | 1941 |         1947  
  2 | 1942 |         1947  
  3 | 1943 |         1947  
  4 | 1944 |         1947  
  5 | 1945 |         1947  
  6 | 1946 |         1947  
(6 rows)  

再次插入几条测试数据,一会你会发现这些记录不存在了。

digoal=> insert into resetxlog_test values (7);  
INSERT 0 1  
digoal=> insert into resetxlog_test values (8);  
INSERT 0 1  
digoal=> insert into resetxlog_test values (9);  
INSERT 0 1  
digoal=> select id,xmin,txid_current() from resetxlog_test ;  
 id | xmin | txid_current   
----+------+--------------  
  1 | 1941 |         1951  
  2 | 1942 |         1951  
  3 | 1943 |         1951  
  4 | 1944 |         1951  
  5 | 1945 |         1951  
  6 | 1946 |         1951  
  7 | 1948 |         1951  
  8 | 1949 |         1951  
  9 | 1950 |         1951  
(9 rows)  

新插入的3条记录的xmin分别是 1948, 1949, 1950 .

digoal=> \q  

停库,准备模拟记录”消失”。

postgres@db5-> pg_ctl stop  
waiting for server to shut down..... done  
server stopped  
postgres@db5-> pg_controldata   
pg_control version number:            903  
Catalog version number:               201105231  
Database system identifier:           5652407581121182719  
Database cluster state:               shut down  
pg_control last modified:             Fri 30 Sep 2011 04:25:26 PM CST  
Latest checkpoint location:           2/40000020  
Prior checkpoint location:            2/3C000020  
Latest checkpoint's REDO location:    2/40000020  
Latest checkpoint's TimeLineID:       7  
Latest checkpoint's NextXID:          0/1952  
Latest checkpoint's NextOID:          24723  
Latest checkpoint's NextMultiXactId:  1  
Latest checkpoint's NextMultiOffset:  0  
Latest checkpoint's oldestXID:        1672  
Latest checkpoint's oldestXID's DB:   24686  
Latest checkpoint's oldestActiveXID:  0  
Time of latest checkpoint:            Fri 30 Sep 2011 04:25:25 PM CST  
Minimum recovery ending location:     0/0  
Backup start location:                0/0  
Current wal_level setting:            hot_standby  
Current max_connections setting:      1000  
Current max_prepared_xacts setting:   0  
Current max_locks_per_xact setting:   64  
Maximum data alignment:               8  
Database block size:                  8192  
Blocks per segment of large relation: 131072  
WAL block size:                       8192  
Bytes per WAL segment:                67108864  
Maximum length of identifiers:        64  
Maximum columns in an index:          32  
Maximum size of a TOAST chunk:        1996  
Date/time type storage:               64-bit integers  
Float4 argument passing:              by value  
Float8 argument passing:              by value  

从这里可以看到NextXID = 1952

下面我们把这个值改成1947。

修改完后记录 id in ( 7,8,9 )应该会 “消失” .

postgres@db5-> pg_resetxlog -x 1947 $PGDATA  
Transaction log reset  
postgres@db5-> pg_ctl start  
server starting  
postgres@db5-> LOG:  loaded library "pg_stat_statements"  
  
postgres@db5-> psql -h 127.0.0.1 digoal digoal  
psql (9.1.0)  
Type "help" for help.  

查询,当然是”消失”的。

digoal=> select id,xmin,txid_current() from resetxlog_test ;  
 id | xmin | txid_current   
----+------+--------------  
  1 | 1941 |         1947  
  2 | 1942 |         1947  
  3 | 1943 |         1947  
  4 | 1944 |         1947  
  5 | 1945 |         1947  
  6 | 1946 |         1947  
(6 rows)  

继续查询 :

digoal=> select id,xmin,txid_current() from resetxlog_test ;  
 id | xmin | txid_current   
----+------+--------------  
  1 | 1941 |         1948  
  2 | 1942 |         1948  
  3 | 1943 |         1948  
  4 | 1944 |         1948  
  5 | 1945 |         1948  
  6 | 1946 |         1948  
(6 rows)  

继续查询,当txid大于记录的xmin后,记录随之出现,这里的7,8就出现了 :

digoal=> select id,xmin,txid_current() from resetxlog_test ;  
 id | xmin | txid_current   
----+------+--------------  
  1 | 1941 |         1950  
  2 | 1942 |         1950  
  3 | 1943 |         1950  
  4 | 1944 |         1950  
  5 | 1945 |         1950  
  6 | 1946 |         1950  
  7 | 1948 |         1950  
  8 | 1949 |         1950  
(8 rows)  

继续查询,当txid大于记录的xmin后,记录随之出现,这里的9就出现了 :

digoal=> select id,xmin,txid_current() from resetxlog_test ;  
 id | xmin | txid_current   
----+------+--------------  
  1 | 1941 |         1951  
  2 | 1942 |         1951  
  3 | 1943 |         1951  
  4 | 1944 |         1951  
  5 | 1945 |         1951  
  6 | 1946 |         1951  
  7 | 1948 |         1951  
  8 | 1949 |         1951  
  9 | 1950 |         1951  
(9 rows)  

另外一个pg_resetxlog的参数-e 是修改控制文件的epoch值。

Latest checkpoint's NextXID:          0/1952  

比如这个是0,输入pg_resetxlog -e 1 后 , PostgreSQL epoch就改为1了。

然后在数据库执行txid_current()的时候会等于xid+2^32次方。

如下 :

postgres@db5-> pg_ctl stop  
waiting for server to shut down..... done  
server stopped  
postgres@db5-> pg_resetxlog -e 1 $PGDATA  
pTransaction log reset  
postgres@db5-> pg_ctl start  
server starting  
postgres@db5-> LOG:  loaded library "pg_stat_statements"  
  
postgres@db5-> psql -h 127.0.0.1 digoal digoal  
psql (9.1.0)  
Type "help" for help.  
  
digoal=> select txid_current();  
 txid_current   
--------------  
   4294969250  
(1 row)  

参考

MVCC

Flag Counter

digoal’s 大量PostgreSQL文章入口