天天看點

postgres 事物可見性測試

可見性的資訊記錄在infomask字段

/*
 * information stored in t_infomask:
 */
#define HEAP_HASNULL                    0x0001  /* has null attribute(s) */
#define HEAP_HASVARWIDTH                0x0002  /* has variable-width attribute(s) */
#define HEAP_HASEXTERNAL                0x0004  /* has external stored attribute(s) */
#define HEAP_HASOID                             0x0008  /* has an object-id field */
#define HEAP_XMAX_KEYSHR_LOCK   0x0010  /* xmax is a key-shared locker */
#define HEAP_COMBOCID                   0x0020  /* t_cid is a combo cid */
#define HEAP_XMAX_EXCL_LOCK             0x0040  /* xmax is exclusive locker */
#define HEAP_XMAX_LOCK_ONLY             0x0080  /* xmax, if valid, is only a locker */

 /* xmax is a shared locker */
#define HEAP_XMAX_SHR_LOCK      (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)

#define HEAP_LOCK_MASK  (HEAP_XMAX_SHR_LOCK | HEAP_XMAX_EXCL_LOCK | \
                                                 HEAP_XMAX_KEYSHR_LOCK)
#define HEAP_XMIN_COMMITTED             0x0100  /* t_xmin committed */
#define HEAP_XMIN_INVALID               0x0200  /* t_xmin invalid/aborted */
#define HEAP_XMIN_FROZEN                (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)
#define HEAP_XMAX_COMMITTED             0x0400  /* t_xmax committed */
#define HEAP_XMAX_INVALID               0x0800  /* t_xmax invalid/aborted */
#define HEAP_XMAX_IS_MULTI              0x1000  /* t_xmax is a MultiXactId */
#define HEAP_UPDATED                    0x2000  /* this is UPDATEd version of row */
#define HEAP_MOVED_OFF                  0x4000  /* moved to another place by pre-9.0
                                                                                 * VACUUM FULL; kept for binary
                                                                                 * upgrade support */
#define HEAP_MOVED_IN                   0x8000  /* moved from another place by pre-9.0
                                                                                 * VACUUM FULL; kept for binary
                                                                                 * upgrade support */
           

若是t_infomask中的HEAP_XMIN_COMMITTED為真,HEAP_XMAX_INVALID為假則說明 這行是新插入的行,是可見的。此時不需要到clog中查詢xmin和xmax的事務狀态;若是沒有設定HEAP_XMIN_COMMITTED,并不能說明 這行沒有送出,而是說明不知道xmin是否送出了,需要到clog中去判斷xmin的狀态。HEAP_XMAX_COMMITTED亦是如此!

當第一次插入資料時,t_infomask中隻設定了HEAP_XMAX_INVALID,HEAP_XMIN_COMMITTED并沒有設定,當再讀取這個資料塊的時候 會通過clog判斷出這些行的事務已經送出,進而設定t_infomask中的HEAP_XMIN_COMMITTED為真。于是當下次我們再查詢該行時,直接使用t_infomask中的HEAP_XMIN_COMMITTED和HEAP_XMAX_INVALID标志位就可以判斷出行的可行性,進而不需要到clog中查詢。

測試過程如下

postgres=# begin;
BEGIN
postgres=# insert into h values (1);
INSERT 0 1
postgres=# select * from heap_page_items(get_raw_page('h',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 |   8160 |        1 |     28 | 2098086 |      0 |        0 | (0,1)  |           1 |       2048 |     24 |        |       | \x01000000
(1 row)

postgres=# 
           

--//目前事物還沒有送出,t_infomask=2048(10進制),轉換為16進制為0x0800

--//0x0800 對應HEAP_XMAX_INVALID

--//送出事務

postgres=# commit;
COMMIT
postgres=# select * from heap_page_items(get_raw_page('h',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 |   8160 |        1 |     28 | 2098086 |      0 |        0 | (0,1)  |           1 |       2048 |     24 |        |       | \x01000000
(1 row)

postgres=#
           

--//送出之後,t_infomask還是2048沒有變化

--//查詢表h

postgres=# select * from h;
 id 
----
  1
(1 row)

postgres=# select * from heap_page_items(get_raw_page('h',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 |   8160 |        1 |     28 | 2098086 |      0 |        0 | (0,1)  |           1 |       2304 |     24 |        |       | \x01000000
(1 row)

postgres=# 
           

--//查詢表h之後,再次檢視t_infomask變成了2304,這步是先讀取clog日志,發現該行事物已經送出,然後把行狀态表示t_infomask從2048改成了2304,轉換為16進制為0x0900

--//0x900 即為HEAP_XMIN_COMMITTED+HEAP_XMAX_INVALID

#define HEAP_XMIN_COMMITTED             0x0100  

#define HEAP_XMAX_INVALID               0x0800  

--//說明事物已經送出了,下次再讀取該行,就不需要去讀clog日志了。

--//再插入一行資料測試

postgres=# insert into h values (2);
INSERT 0 1
postgres=# select * from heap_page_items(get_raw_page('h',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 |   8160 |        1 |     28 | 2098086 |      0 |        0 | (0,1)  |           1 |       2304 |     24 |        |       | \x01000000
  2 |   8128 |        1 |     28 | 2098087 |      0 |        0 | (0,2)  |           1 |       2048 |     24 |        |       | \x02000000
(2 rows)

postgres=# 
           

--//删除clog的日志

[[email protected] pg11]$ cd pg_xact/
[[email protected] pg_xact]$ ls
0000  0001  0002
[[email protected] pg_xact]$ mkdir bk
[[email protected] pg_xact]$ mv 000* bk
[[email protected] pg_xact]$ ls
bk
[[email protected] pg_xact]$ 
           

--//關閉啟動資料庫時,日志報錯

2020-12-04 21:14:59.607 EST,,,65969,,5fcaed23.101b1,1,,2020-12-04 21:14:59 EST,,0,LOG,00000,"ending log output to stderr",,"Future log output will go to log destination ""csvlog"".",,,,,,,""
2020-12-04 21:14:59.610 EST,,,65971,,5fcaed23.101b3,1,,2020-12-04 21:14:59 EST,,0,LOG,00000,"database system was shut down at 2020-12-04 21:14:19 EST",,,,,,,,,""
2020-12-04 21:14:59.614 EST,,,65971,,5fcaed23.101b3,2,,2020-12-04 21:14:59 EST,,0,FATAL,58P01,"could not access status of transaction 2098089","Could not open file ""pg_xact/0002"": No such file or directory.",,,,,,,,""
2020-12-04 21:14:59.614 EST,,,65969,,5fcaed23.101b1,2,,2020-12-04 21:14:59 EST,,0,LOG,00000,"startup process (PID 65971) exited with exit code 1",,,,,,,,,""
2020-12-04 21:14:59.614 EST,,,65969,,5fcaed23.101b1,3,,2020-12-04 21:14:59 EST,,0,LOG,00000,"aborting startup due to startup process failure",,,,,,,,""
2020-12-04 21:14:59.616 EST,,,65969,,5fcaed23.101b1,4,,2020-12-04 21:14:59 EST,,0,LOG,00000,"database system is shut down",,,,,,,,,""
           

--//重建clog檔案,但是存在上面的事務會丢失

[[email protected] pg_xact]$ dd if=/dev/zero of=0002 bs=256k count=1
1+0 records in
1+0 records out
262144 bytes (262 kB) copied, 0.000449713 s, 583 MB/s
[[email protected] pg_xact]$ ls -ltr
total 260
drwxrwxr-x 2 postgres postgres   4096 Dec  4 21:14 bk
-rw-rw-r-- 1 postgres postgres 262144 Dec  4 21:20 0002
[[email protected] pg_xact]$ 
           

--//再次啟動不會報錯

[[email protected] log]$ pg_ctl start
waiting for server to start....2020-12-04 21:20:19.103 EST [66760] LOG:  listening on IPv4 address "0.0.0.0", port 5442
2020-12-04 21:20:19.103 EST [66760] LOG:  listening on IPv6 address "::", port 5442
2020-12-04 21:20:19.104 EST [66760] LOG:  listening on Unix socket "/tmp/.s.PGSQL.5442"
2020-12-04 21:20:19.116 EST [66760] LOG:  redirecting log output to logging collector process
2020-12-04 21:20:19.116 EST [66760] HINT:  Future log output will appear in directory "log".
 done
server started
           

--//查詢插入的資料丢失

[[email protected] log]$ p
psql (11.7)
Type "help" for help.

postgres=# select * from h;
 id 
----
  1
(1 rows)

postgres=# 
           

--//通過pageinspect 檢視t_maskinfo的狀态變成了2560,對應16進制為A00

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 |   8160 |        1 |     28 | 2098086 |      0 |        0 | (0,1)  |           1 |       2304 |     24 |        |       | \x01000000
  2 |   8128 |        1 |     28 | 2098087 |      0 |        0 | (0,2)  |           1 |       2560 |     24 |        |       | \x02000000
           

--//A00對應如下

#define HEAP_XMIN_INVALID               0x0200 

#define HEAP_XMAX_INVALID               0x0800

因為xmin 和xmax invalid的掩碼都被設定了, 是以處理這些記錄的事務還未送出或者已經abort. 當然是看不到的.

如果已送出事務, 應該有這兩個标記之一 :

#define HEAP_XMIN_COMMITTED             0x0100    

#define HEAP_XMAX_COMMITTED             0x0400