PostgreSQL技術大講堂 - 第21講:行可見性規(guī)則

PostgreSQL從小白到專家,是從入門逐漸能力提升的一個系列教程,內(nèi)容包括對PG基礎的認知、包括安裝使用、包括角色權限、包括維護管理、、等內(nèi)容,希望對熱愛PG、學習PG的同學們有幫助,歡迎持續(xù)關注CUUG PG技術大講堂。
第21講:行可見性規(guī)則
內(nèi)容1:PostgreSQL事務id介紹
內(nèi)容2:PostgreSQL DML操作原理
內(nèi)容3:事務快照在可見性規(guī)則中的作用
內(nèi)容4:T_xmin狀態(tài)對于可見性規(guī)則判斷的重要度
內(nèi)容5:常見的行可見性規(guī)則的介紹
內(nèi)容6:實現(xiàn)閃回功能
TXID介紹
· 事務id(txid)
當一個事務開始時,PostgreSQL中的事務管理系統(tǒng)會為該事務分配一個唯一標識符,即事務ID(txid).PostgreSQL中的txid被定義為一個32位的無符號整數(shù),也就是說,它能記錄大約42億個事務。通常txid對我們是透明的,但是我們可以利用PostgreSQL內(nèi)部的函數(shù)來獲取當前事務的txid。
事務ID用來標識一個事務的先后順序,該順序決定了鎖申請的優(yōu)先權,已經(jīng)訪問一張表時對行的可見性規(guī)則判斷。
testdb=# SELECT txid_current();
txid_current
--------------
100
(1 row)
Tuples Structure
· 元組(行)結構
t_xmin保存插入此元組的事務的txid,它的狀態(tài)是行可見性判斷關鍵的依據(jù)。
t_xmax保存刪除或更新此元組的事務的txid。如果此元組未被刪除或更新,則t_xmax設置為0,這意味著無效,它的狀態(tài)也是行可見性判斷關鍵的依據(jù)。

DML操作原理
· Insertion

· Deletion

· Update
執(zhí)行第一個更新命令時,通過將txid 100設置為t_xmax,邏輯上刪除Tuple_1,然后插入Tuple_2。然后,將元組1的t_ctid重寫為指向元組2。
當執(zhí)行第二個UPDATE命令時,與第一個UPDATE命令一樣,Tuple_2在邏輯上被刪除,Tuple_3被插入。

事務狀態(tài)
· 四種事務狀態(tài)
IN_PROGRESS
COMMITTED
ABORTED
SUB_COMMITTED
Commit Log
· 事務狀態(tài)記錄方式

事務快照
· 事務快照概述
事務快照是一個數(shù)據(jù)集,用于存儲有關單個事務在某個時間點上是否所有事務都處于活動狀態(tài)的信息。在這里,活動事務表示它正在進行或尚未啟動。txid_current_snapshot的文本表示為“xmin:xmax:xip_list”,組件描述如下:
Xmin:最早仍在活動的txid。所有以前的事務要么提交并可見,要么回滾并停止。
Xmax:第一個尚未分配的txid。截至快照時,所有大于或等于此值的txid尚未啟動,此不可見。
xip_list:快照時的活動txid。該列表僅包含xmin和xmax之間的活動txid。
testdb=# SELECT txid_current_snapshot();
txid_current_snapshot
-----------------------
100:104:100,102
(1 row)
例如,在快照'100:104:100,102'中,xmin是'100',xmax是'104',xip_list是'100,102'。
可見性規(guī)則世界觀
· 事務快照在可見性規(guī)則中的意義
富有哲理性的判斷規(guī)則:過去發(fā)生過的為可見,將來未發(fā)生的為不可見。

行可見性判斷重要因素
· 可見性判斷的重要因素
可見性檢查規(guī)則是一組規(guī)則,關鍵的判斷因素有:t_xmin、t_xmax、clog和獲取的事務快照確定每個元組是否可見。
T_xmin的三種狀態(tài)ABORTED、IN_PROGRESS、COMMITTED是判斷的第一前提條件。

ABORTED狀態(tài)
· t_xmin =ABORTED
t_xmin =ABORTED,則判斷此行不可見
/* t_xmin status = ABORTED */
Rule 1: IF t_xmin status is 'ABORTED' THEN
RETURN 'Invisible'
END IF
IN_PROGRESS狀態(tài)
· t_xmin=IN_PROGRESS
t_xmin=IN_PROGRESS,當前事務可見,其它事務不可見
/* t_xmin status = IN_PROGRESS */
IF t_xmin status is 'IN_PROGRESS' THEN
IF t_xmin = current_txid THEN
Rule 2: IF t_xmax = INVALID THEN
RETURN 'Visible'
Rule 3: ELSE /* this tuple has been deleted or updated by the current transaction itself. */
RETURN 'Invisible'
END IF
Rule 4: ELSE /* t_xmin ≠ current_txid */
RETURN 'Invisible'
END IF
END IF
COMMITTED狀態(tài)
· t_xmin=COMMITTED
t_xmin=COMMITTED,此狀態(tài)判斷時還得看t_xmax的值,如果t_xmax的值為0,則此行可見;如果不為0,那么判斷時還得看t_xmax的狀態(tài)是當前事務還是非當前事務,判斷規(guī)則就比較復雜。
/* t_xmin status = COMMITTED */
IF t_xmin status is 'COMMITTED' THEN
Rule 5: IF t_xmin is active in the obtained transaction snapshot THEN
RETURN 'Invisible'
Rule 6: ELSE IF t_xmax = INVALID OR status of t_xmax is 'ABORTED' THEN
RETURN 'Visible'
ELSE IF t_xmax status is 'IN_PROGRESS' THEN
Rule 7: IF t_xmax = current_txid THEN
RETURN 'Invisible'
Rule 8: ELSE /* t_xmax ≠ current_txid */
RETURN 'Visible'
END IF
ELSE IF t_xmax status is 'COMMITTED' THEN
Rule 9: IF t_xmax is active in the obtained transaction snapshot THEN
RETURN 'Visible'
Rule 10: ELSE
RETURN 'Invisible'
END IF
END IF
END IF
可見性判斷概述
· 可見性判斷示例

R6判斷規(guī)則
· T3 時根據(jù)規(guī)則6進行判斷
Rule6(Tuple_1)?Status(t_xmin:199) = COMMITTED ∧ t_xmax = INVALID ?Visible
T_xmin=commit,并且t_xman=0,該行對于所有的事務均可見

R7與R2判斷規(guī)則
· T5時事務ID為200的根據(jù)規(guī)則7、2進行判斷
Rule7(Tuple_1): Status(t_xmin:199) = COMMITTED ∧ Status(t_xmax:200) = IN_PROGRESS ∧ t_xmax:200 = current_txid:200 ? Invisible
Rule2(Tuple_2): Status(t_xmin:200) = IN_PROGRESS ∧ t_xmin:200 = current_txid:200 ∧ t_xmax = INVAILD ? Visible
此時塊中包含兩行數(shù)據(jù),對于事務id=200來說,它的判斷規(guī)則是:
第一行數(shù)據(jù)根據(jù)規(guī)則7判斷,t_xmin=commit,同時(t_xmax=200)= IN_PROGRESS,并且t_xmax:200為當前事務id,則第一行判斷為不可見。
第二行根據(jù)規(guī)則2判斷, t_xmin=commit,同時(t_xmax=200)為當前事務id,并且t_xmax為無效,則該行可見。

testdb=# -- txid 200
testdb=# SELECT * FROM tbl;
name
------
Hyde
R8與R4判斷規(guī)則
· T5時事務ID為201的根據(jù)規(guī)則8、4進行判斷
Rule8(Tuple_1): Status(t_xmin:199) = COMMITTED ∧ Status(t_xmax:200) = IN_PROGRESS ∧ t_xmax:200 ≠ current_txid:201 ? Visible
Rule4(Tuple_2): Status(t_xmin:200) = IN_PROGRESS ∧ t_xmin:200 ≠ current_txid:201 ? Invisible
此時塊中包含兩行數(shù)據(jù),對于事務id=201來說,它的判斷規(guī)則是:
第一行數(shù)據(jù)根據(jù)規(guī)則8判斷,t_xmin=commit,同時(t_xmax=200)= IN_PROGRESS,并且t_xmax:200不是當前事務id,則第一行判斷為可見。
第二行根據(jù)規(guī)則2判斷, (t_xmax=200)狀態(tài)為IN_PROGRESS,同時t_xmin不是當前事務id,則該行不可見。

testdb=# -- txid 201
testdb=# SELECT * FROM tbl;
name
--------
Jekyll
R10與R6判斷規(guī)則
· T7時事務ID為201的根據(jù)規(guī)則10、6進行判斷(READ COMMITED)
Rule10(Tuple_1): Status(t_xmin:199) = COMMITTED ∧ Status(t_xmax:200) = COMMITTED ∧ Snapshot(t_xmax:200) ≠ active ? Invisible
Rule6(Tuple_2): Status(t_xmin:200) = COMMITTED ∧ t_xmax = INVALID ? Visible
T7時事務id為200的提交了事務,對于事務id=201來說,它的判斷規(guī)則是:
第一行根據(jù)規(guī)則10判斷,t_xmin=commit,同時(t_xmax=200)= COMMITTED ,并且Snapshot(t_xmax:200) 狀態(tài)為非活動,則第一行判斷為不可見。
第二行根據(jù)規(guī)則6判斷, (t_xmax=200)狀態(tài)為COMMITTED ,同時t_xmax為無效,則該行可見。

testdb=# -- txid 201 (READ COMMITTED)
testdb=# SELECT * FROM tbl;
name
------
Hyde
R9與R5判斷規(guī)則
· T7時事務ID為201的根據(jù)規(guī)則9、5進行判斷(REPEATABLE READ)
Rule9(Tuple_1): Status(t_xmin:199) = COMMITTED ∧ Status(t_xmax:200) = COMMITTED ∧ Snapshot(t_xmax:200) = active ? Visible
Rule5(Tuple_2): Status(t_xmin:200) = COMMITTED ∧ Snapshot(t_xmin:200) = active ? Invisible
如果事務的隔離級別是可重復讀,那么其判斷規(guī)則就會發(fā)生變化,T7時事務id為200的提交了事務,對于事務id=201來說,它的判斷規(guī)則是:
第一行根據(jù)規(guī)則9判斷,t_xmin=commit,同時(t_xmax=200)= COMMITTED ,并且Snapshot(t_xmax:200) 狀態(tài)為活動,則第一行判斷為可見。
第二行根據(jù)規(guī)則5判斷, t_xmax=200狀態(tài)為COMMITTED , Snapshot(t_xmin:200) 為活動,則該行不可見,通過該規(guī)則,不會導致幻讀發(fā)生。

testdb=# -- txid 201 (REPEATABLE READ)
testdb=# SELECT * FROM tbl;
name
--------
Jekyll
提高判斷效率
· Hint Bits
由于進行行可見性判斷時都要查看存儲在clog中t_xmin和t_xmax的狀態(tài),為了解決對clog頻繁訪問這個問題,PostgreSQL使用了提示位,如下所示:
#define HEAP_XMIN_COMMITTED 0x0100 /* t_xmin committed */
#define HEAP_XMIN_INVALID 0x0200 /* t_xmin invalid/aborted */
#define HEAP_XMAX_COMMITTED 0x0400 /* t_xmax committed */
#define HEAP_XMAX_INVALID 0x0800 /* t_xmax invalid/aborted */
實現(xiàn)閃回功能
PostgreSQL由于數(shù)據(jù)的更新時新舊數(shù)據(jù)都保留在數(shù)據(jù)塊中,那么如果要實現(xiàn)像Oracle一樣的閃回查詢功能應該是可以實現(xiàn)的,只要在判斷時先判斷該查詢是否是閃回查詢,然后再根據(jù)一個針對閃回查詢的可見性規(guī)則判斷就可以實現(xiàn)。
如果實現(xiàn)閃回查詢,那么涉及到Vacuum操作時需要考慮更多的因素,需要有一個參數(shù)來設置塊中被刪除的行保留的時間長度。

以上就是【PostgreSQL從小白到專家】第21講 - 行可見性規(guī)則 的內(nèi)容,歡迎一起探討交流,往期視頻及文檔,聯(lián)系CUUG