PostgreSQL從小白到專家,是從入門逐漸能力提升的一個系列教程,內容包括對PG基礎的認知、包括安裝使用、包括角色權限、包括維護管理、、等內容,希望對熱愛PG、學習PG的同學們有幫助,歡迎持續關注CUUG PG技術大講堂。
第21講:行可見性規則
內容1:PostgreSQL事務id介紹
內容2:PostgreSQL DML操作原理
內容3:事務快照在可見性規則中的作用
內容4:T_xmin狀態對于可見性規則判斷的重要度
內容5:常見的行可見性規則的介紹
內容6:實現閃回功能
TXID介紹
· 事務id(txid)
當一個事務開始時,PostgreSQL中的事務管理系統會為該事務分配一個唯一標識符,即事務ID(txid).PostgreSQL中的txid被定義為一個32位的無符號整數,也就是說,它能記錄大約42億個事務。通常txid對我們是透明的,但是我們可以利用PostgreSQL內部的函數來獲取當前事務的txid。
事務ID用來標識一個事務的先后順序,該順序決定了鎖申請的優先權,已經訪問一張表時對行的可見性規則判斷。
testdb=# SELECT txid_current();
txid_current
--------------
100
(1 row)
Tuples Structure
· 元組(行)結構
t_xmin保存插入此元組的事務的txid,它的狀態是行可見性判斷關鍵的依據。
t_xmax保存刪除或更新此元組的事務的txid。如果此元組未被刪除或更新,則t_xmax設置為0,這意味著無效,它的狀態也是行可見性判斷關鍵的依據。
DML操作原理
· Insertion
· Deletion
· Update
執行第一個更新命令時,通過將txid 100設置為t_xmax,邏輯上刪除Tuple_1,然后插入Tuple_2。然后,將元組1的t_ctid重寫為指向元組2。
當執行第二個UPDATE命令時,與第一個UPDATE命令一樣,Tuple_2在邏輯上被刪除,Tuple_3被插入。
事務狀態
· 四種事務狀態
IN_PROGRESS
COMMITTED
ABORTED
SUB_COMMITTED
Commit Log
· 事務狀態記錄方式
事務快照
· 事務快照概述
事務快照是一個數據集,用于存儲有關單個事務在某個時間點上是否所有事務都處于活動狀態的信息。在這里,活動事務表示它正在進行或尚未啟動。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'。
可見性規則世界觀
· 事務快照在可見性規則中的意義
富有哲理性的判斷規則:過去發生過的為可見,將來未發生的為不可見。
行可見性判斷重要因素
· 可見性判斷的重要因素
可見性檢查規則是一組規則,關鍵的判斷因素有:t_xmin、t_xmax、clog和獲取的事務快照確定每個元組是否可見。
T_xmin的三種狀態ABORTED、IN_PROGRESS、COMMITTED是判斷的第一前提條件。
ABORTED狀態
· 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_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_xmin=COMMITTED
t_xmin=COMMITTED,此狀態判斷時還得看t_xmax的值,如果t_xmax的值為0,則此行可見;如果不為0,那么判斷時還得看t_xmax的狀態是當前事務還是非當前事務,判斷規則就比較復雜。
/* 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判斷規則
· T3 時根據規則6進行判斷
Rule6(Tuple_1)=>Status(t_xmin:199) = COMMITTED ∧ t_xmax = INVALID =>Visible
T_xmin=commit,并且t_xman=0,該行對于所有的事務均可見
R7與R2判斷規則
· T5時事務ID為200的根據規則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
此時塊中包含兩行數據,對于事務id=200來說,它的判斷規則是:
第一行數據根據規則7判斷,t_xmin=commit,同時(t_xmax=200)= IN_PROGRESS,并且t_xmax:200為當前事務id,則第一行判斷為不可見。
第二行根據規則2判斷, t_xmin=commit,同時(t_xmax=200)為當前事務id,并且t_xmax為無效,則該行可見。
testdb=# -- txid 200
testdb=# SELECT * FROM tbl;
name
------
Hyde
R8與R4判斷規則
· T5時事務ID為201的根據規則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
此時塊中包含兩行數據,對于事務id=201來說,它的判斷規則是:
第一行數據根據規則8判斷,t_xmin=commit,同時(t_xmax=200)= IN_PROGRESS,并且t_xmax:200不是當前事務id,則第一行判斷為可見。
第二行根據規則2判斷, (t_xmax=200)狀態為IN_PROGRESS,同時t_xmin不是當前事務id,則該行不可見。
testdb=# -- txid 201
testdb=# SELECT * FROM tbl;
name
--------
Jekyll
R10與R6判斷規則
· T7時事務ID為201的根據規則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來說,它的判斷規則是:
第一行根據規則10判斷,t_xmin=commit,同時(t_xmax=200)= COMMITTED ,并且Snapshot(t_xmax:200) 狀態為非活動,則第一行判斷為不可見。
第二行根據規則6判斷, (t_xmax=200)狀態為COMMITTED ,同時t_xmax為無效,則該行可見。
testdb=# -- txid 201 (READ COMMITTED)
testdb=# SELECT * FROM tbl;
name
------
Hyde
R9與R5判斷規則
· T7時事務ID為201的根據規則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
如果事務的隔離級別是可重復讀,那么其判斷規則就會發生變化,T7時事務id為200的提交了事務,對于事務id=201來說,它的判斷規則是:
第一行根據規則9判斷,t_xmin=commit,同時(t_xmax=200)= COMMITTED ,并且Snapshot(t_xmax:200) 狀態為活動,則第一行判斷為可見。
第二行根據規則5判斷, t_xmax=200狀態為COMMITTED , Snapshot(t_xmin:200) 為活動,則該行不可見,通過該規則,不會導致幻讀發生。
testdb=# -- txid 201 (REPEATABLE READ)
testdb=# SELECT * FROM tbl;
name
--------
Jekyll
提高判斷效率
· Hint Bits
由于進行行可見性判斷時都要查看存儲在clog中t_xmin和t_xmax的狀態,為了解決對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 */
實現閃回功能
PostgreSQL由于數據的更新時新舊數據都保留在數據塊中,那么如果要實現像Oracle一樣的閃回查詢功能應該是可以實現的,只要在判斷時先判斷該查詢是否是閃回查詢,然后再根據一個針對閃回查詢的可見性規則判斷就可以實現。
如果實現閃回查詢,那么涉及到Vacuum操作時需要考慮更多的因素,需要有一個參數來設置塊中被刪除的行保留的時間長度。
以上就是【PostgreSQL從小白到專家】第21講 - 行可見性規則 的內容,歡迎進群一起探討交流
QQ交流群:752027153
微信交流群:聯系客服拉你進微信PG交流群
釘釘交流群:35822460,釘釘群專門有視頻講解