可見性的資訊記錄在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