併發性是一種機制,用於在資料庫中同時執行多個事務時保持原子性和隔離性,這是 ACID 的兩個屬性。
併發控制技術主要有三種型別:多版本併發控制(MVCC)、嚴格兩階段鎖定(S2PL)和樂觀併發控制(OCC)。 每種技術都有許多變化。 在 MVCC 中,每個寫入操作都會建立資料項的新版本,同時保留舊版本。 當事務讀取資料項時,會選擇其中乙個版本來確保單個事務的隔離。 MVCC的主要優點是“讀取器不會阻止寫入器,寫入器也不會阻止讀取器”,相比之下,例如,基於S2PL的系統在寫入器寫入專案時必須阻止讀取器,因為寫入器獲得了該項目的排他性鎖。 PostgreSQL 和一些 RDBMS 使用 MVCC 的變體,稱為快照隔離 (SI)。
為了實現 SI,一些 RDBMS(例如 Oracle)使用回滾段。 寫入新資料項時,舊版本的資料項將寫入撤消段,新資料項隨後將覆蓋到資料區域中。 PostgreSQL 使用更簡單的方法。 新資料項將直接插入到相關表頁中。 讀取專案時,PostgreSQL 通過應用可見性檢查規則來選擇專案的適當版本來響應單個事務。
SI 不允許 ANSI SQL-92 標準中定義的三種例外:髒讀、不可重複讀和幻像讀。 但是,SI 不能真正可序列化,因為它允許序列化異常,例如寫入傾斜和唯讀事務傾斜。 請注意,基於可序列性經典定義的 ANSI SQL-92 標準與當前理論中的定義不同。 若要解決此問題,請從版本 9 開始新增了 1 個序列快照隔離 (SSI)。 SSI 可以檢測序列化異常,並可以解決由此類異常引起的衝突。 所以,postgresql 9版本 1 或更高版本提供真正的可序列化隔離級別。 (SQL Server 也使用 SSI; Oracle 仍然只使用 SI。 )
PostgreSQL 中的事務隔離級別。
下表描述了 PostgreSQL 實現的事務隔離級別:
1:在版本 9 中0 及更早版本,此級別已用作“可序列化”,因為它不允許 ANSI SQL-92 標準中定義的三個異常。 但是,在版本 9 中SSI 在 1 中的實現,已更改為“可重複讀取”,並引入了真正的可序列化級別。
每當事務啟動時,事務管理器都會分配乙個稱為事務 ID (txid) 的唯一識別符號。 PostgreSQL 的 txid 是乙個 32 位無符號整數,約為 42 億(一千萬)。 如果內建的 txid current() 函式在交易開始後執行,該函式將返回當前 txid,如下所示:
testdb=# begin;begintestdb=# select txid_current();txid_current---825(1 row)
PostgreSQL 保留以下三個特殊的 txid:
0 表示 txid 無效。
1 表示引導 txid,僅在初始化資料庫集群時使用。
2 表示凍結的 txid。
Txids 可以相互比較。 例如,從 txid 100 的角度來看,txid 大於 100 是“未來”,從 txid 100 看不到; 小於 100 的 Txid 是“過去”和可見的(圖 1)。1 a))。
圖1PostgreSQL 中的 1 個事務 ID
由於實際系統中缺少 txid 空間,PostgreSQL 將 txid 空間視為乙個圓。 前 21 億個 txid 是“過去”,接下來的 21 億個 txid 是“未來”(圖 1)。1 b)。
請注意,begin 命令不會分配 txid。 在 PostgreSQL 中,當執行 begin 命令後執行第乙個命令時,事務管理器會分配乙個 tixd 並啟動事務。
表頁中有兩種型別的堆元組:普通資料元組和 Toast 元組。 本節僅介紹常用的元組。
堆元組由三部分組成:heaptupleheaderdata 結構、null 點陣圖和使用者資料(圖 1)。2)。
heaptupleheaderdata 結構包含 7 個字段,但後續部分只需要其中 4 個字段:
t xmin 儲存插入元組的事務的 txid。 t xmax 儲存刪除或更新此元組的事務的 txID。 如果未刪除或更新此元組,則 t xmax 設定為 0,表示無效。 T CID 儲存命令 ID (cid),即在當前事務中執行此命令之前執行的 SQL 命令數,從 0 開始。 例如,假設我們在單個事務中執行三個插入命令:'begin;insert;insert;insert;commit;'。如果第乙個命令插入此元組,則 t cid 設定為 0。 如果第二個命令插入此元組,則 t cid 設定為 1,依此類推。 t ctid 包含指向自身或新元組的元組識別符號 (TID)。 TID,如第 3 節所述部分,用於標識表中的元組。 更新元組時,元組的 t ctid 指向新元組; 否則,t ctid 將指向自身。
1. 安裝 testdb= create extension pageinspect; create extension2、 testdb= create table t1 as select * from (select row number() over() as row num,oid from pg class) t where rownum<2; select 13, TESTDB= select lp as tuple, t xmin, t xmax, t field3 as t cid, t ctid從堆頁面項(get raw page('t1', 0));tuple | t_xmin | t_xmax | t_cid | t_ctid---1 | 850 | 871 | 0 | 0,1)2 | 851 | 854 | 1 | 0,3)3 | 854 | 863 | 0 | 0,4)4 | 863 | 865 | 0 | 0,5)5 | 865 | 869 | 0 | 0,7)6 | 866 | 0 | 0 | 0,6)7 | 869 | 0 | 0 | 0,7)(7 rows)
沒有外掛程式,請檢視 pg 屬性
1. 查詢 PG 屬性表,檢視新增到表中的系統列,以及兩個列 ID 和 Name:
postgres=# select attname, format_type (atttypid,atttypmod) from pg_attributewhere attrelid = 'foo.bar'::regclass::oid order by attnum;attname | format_type---tableoid | oidcmax | cidxmax | xidcluster management techniquescmin | cidxmin | xidctid | tid id | integername | character varying(5) (8 rows)
ctid 查詢每個元組的位置
通過顯式查詢 xmin,我們可以通過查詢每條記錄的 xmin 值來檢視插入記錄的交易 ID。 請注意以下日誌中所有記錄的 xmin 值:
我們還可以通過顯式選擇每條記錄來找到它的 xmax。 如果 xmax 為 0,則它從未被刪除並且是可見的。
CMIN 和 CMAX 簡介 CMIN:在事務中插入命令識別符號(從 0 開始)。 cmax:刪除事務中的命令識別符號(從 0 開始)。 CMIN 和 CMAX 都是表示元組的命令 ID,即 CMIN 是生成元組的命令 ID,cmax 是刪除元組的命令 ID。 cmin表示插入資料的命令ID,cmax表示刪除資料。 那麼,您如何知道它是被字段插入還是刪除的呢? 要解決這個問題,我們需要了解組合cid。
當交易中只插入資料時,t cid 會儲存 cmin,因為此時只有 cmin 有效。 執行更新或刪除操作時,將生成 cmax。 當同時存在 cmin 和 cmax 時,即當同一事務中同時存在插入和更新時,t cid 儲存組合 cid,當事務中同時存在插入和更新時,t cid 儲存組合 cid。
本節介紹如何插入、刪除和更新元組。 然後,簡要描述用於插入和更新元組的自由空間對映 (FSM)。
為了重點介紹元組,下面不再表示標題和行指標。 圖3顯示了如何表示元組的示例。
圖13 個元組的表示形式。
通過插入操作,將新元組直接插入到目標表的頁面中(圖 1)。4)。
圖14 元組插入。
假設 txid 為 99 的事務在頁面中插入乙個元組。 此時,插入的元組的標頭字段設定如下。
元組 1:t xmin 設定為 99,因為元組是由 txid 99 插入的。
t xmax 設定為 0,因為元組尚未刪除或更新。
T CID 設定為 0,因為元組是 txid 99 插入的第乙個元組。
t ctid 設定為 (0,1) 並指向自身,因為這是最新的元組。
在刪除操作期間,目標元組將邏輯刪除。 執行 delete 命令的 txid 的值設定為元組的 txmax(圖 1)。5)。
圖15。元組刪除。
假設元組 1 被 txid 111 刪除。 此時,元組 1 的標頭字段設定如下:
元組 1:T xmax 設定為 111。
如果提交了 txid 111,則不再需要元組 1。 通常,不需要的元組在 PostgreSQL 中稱為死元組。
死元組最終應從頁面中刪除。 清理死元組稱為真空處理,將在第 6 章中介紹。
在更新操作中,PostgreSQL 在邏輯上刪除最新的元組並插入乙個新的元組(圖 1)。6)。
圖16 更新行兩次。
假設 txid 99 插入的行被 txid 100 更新了兩次。
執行第乙個更新命令時,通過將 t xmax 設定為 txid 100 來插入邏輯刪除元組 1,然後插入元組 2。 然後重寫元組 1 的 t ctid 以指向元組 2。 元組 1 和元組 2 的標頭字段如下所示:
元組 1:t xmax 設定為 100。
t ctid 從 (0, 1) 重寫為 (0, 2)。
元組 2:t xmin 設定為 100。
t xmax 設定為 0。
t cid 設定為 0。
t ctid 設定為 (0,2)。
執行第二個更新命令時,元組 2 將邏輯刪除,元組 3 將插入,就像在第乙個更新命令中一樣。 元組 2 和元組 3 的標頭字段如下所示:
元組 2:T xmax 設定為 100。
t ctid 從 (0, 2) 重寫為 (0, 3)。
元組 3:t xmin 設定為 100。
t xmax 設定為 0。
t cid 設定為 1。
t ctid 設定為 (0,3)。
與刪除操作一樣,如果提交了 txid 100,則元組 1 和元組 2 將成為死元組,如果 txid 100 中止,元組 2 和元組 3 將成為死元組。
在插入堆或索引元組時,PostgreSQL使用對應表或索引的FSM來選擇可以插入的頁面。
如 1 中所述2.如第 3 節所述,所有表和索引都有自己的 FSM。 每個 FSM 在相應的表或索引檔案中儲存有關每個頁的可用空間量的資訊。
所有 FSM 都以“fsm”字尾儲存,如有必要,它們將載入到共享記憶體中。
pg_freespacemap
擴充套件的 PG FreespaceMap 為指定的表索引提供可用空間。 以下查詢顯示指定表中每頁的可用空間比率。
testdb=# create extension pg_freespacemap;create extensiontestdb=# select *,round(100 * ail/8192 ,2) as "freespace ratio"from pg_freespace('accounts'); blkno | ail | freespace ratio---0 | 7904 | 96.00 1 | 7520 | 91.00 2 | 7136 | 87.00 3 | 7136 | 87.00 4 | 7136 | 87.00 5 | 7136 | 87.00
PostgreSQL 將事務的狀態儲存在提交日誌中。 提交日誌(通常稱為 clog)被分配到共享記憶體並在整個事務中使用。
本節介紹 PostgreSQL 中事務的狀態、堵塞的執行方式以及堵塞的維護方式。
PostgreSQL 定義了四種事務狀態:進行中、已提交、已中止和子已提交。
前三種狀態是不言自明的。 例如,當事務正在進行時,其狀態為“正在進行”。
Sub Committed 用於子事務,本文件省略了其描述。
堵塞塊由共享記憶體中的乙個或多個 8 KB 頁面組成。 它在邏輯上形成乙個陣列,其中陣列的索引對應於它們各自的交易 ID,陣列中的每個專案都儲存著相應交易 ID 的狀態。 圖1圖7顯示了堵塞及其工作原理。
圖17 堵塞的工作原理。
T1:txid 200 次提交; txid 200 的狀態已從“進行中”更改為“已提交”。
T2:TXID 201 中止; txid 201 的狀態已從“正在進行”更改為“已中止”。
當當前 txid 前進並且 clog 無法再儲存它時,將附加乙個新頁面。
當需要事務狀態時,將呼叫內部函式。 這些函式讀取堵塞並返回所請求事務的狀態。
當 PostgreSQL 服務關閉或檢查點程序執行時,堵塞資料將寫入儲存在 PG XACT 子目錄中的檔案中。 (請注意,PG XACT 為 9。6 或更早版本稱為 pg clog。 這些檔案被命名等。 最大檔案大小為 256 KB。 例如,如果乙個木屐使用了 8 頁(第 1 頁到第 8 頁,總大小為 64 kb),則其資料將寫入 0000 (64 kb)。 如果 clog 使用 37 頁 (296 kb),則其資料將寫入 0000 和 0001,其大小分別為 256 kb 和 40 kb。
當 PostgreSQL 啟動時,將載入儲存在 pg xact 檔案中的資料以初始化 clog。
堵塞的大小不斷增加,因為每當填充堵塞時都會附加新頁面。 但是,並非堵塞中的所有資料都是必需的。 真空處理會定期刪除此類舊資料(堵塞頁面和檔案)。
事務快照是乙個資料集,用於儲存有關單個事務的所有事務在某個時間點是否處於活動狀態的資訊。 這裡的活動事務意味著它正在進行中或尚未開始。
postgresql 內部定義的事務快照的文字表示格式為“100:100:”。 例如,“100:100:”表示“小於 99 的 txid 處於非活動狀態,等於或大於 100 的 txid 處於活動狀態”。
內建函式 pg 當前快照及其文字表示格式。
函式 pg current snapshot 顯示當前事務的快照。
testdb=# select pg_current_snapshot();pg_current_snapshot---100:104:100,102(1 row)
txid 當前快照的文字表示為“xmin:xmax:xip list”,其元件說明如下:
xmin 仍處於活動狀態的最早 txid):所有較舊的事務要麼被提交和可見,要麼被回滾和終止。
xmax 第乙個尚未分配的 txid):所有大於或等於該值的 txid 在快照時尚未啟動,因此不可見。
xip_list
快照時的活動事務 ID 列表):此列表僅包含介於 xmin 和 xmax 之間的活動事務 ID。
例如,在快照“100:104:100,102”中,xmin 為“100”,xmax 為“104”,xip list 為“100,102”。
以下是兩個具體示例:
圖18 事務快照表示示例。
第乙個示例是“100:100:”。 快照的含義如下(圖 1)。8(a)):
等於或小於 99 的 Txid 不處於活動狀態,因為 XMIN 為 100。
等於或大於 100 的 Txid 處於活動狀態,因為 xmax 為 100。
第二個示例是“100:104:100,102”。 快照的含義如下(圖 1)。8(b)):
等於或小於 99 的 Txid 處於非活動狀態。
等於或大於 104 的 Txid 處於活動狀態。
txid 100 和 102 處於活動狀態,因為它們存在於 XIP 列表中,而 txID 101 和 103 處於非活動狀態。
事務快照由事務管理器提供。 在“已提交讀”隔離級別,每次執行 SQL 命令時都會拍攝快照; 否則(可重複讀取或可序列化),事務僅在執行第乙個 SQL 命令時生成快照。 獲取到的事務快照用於元組的可見性檢查,如 1 所示第7節.
當捕獲的快照用於可見性檢查時,必須考慮快照中的活動事務正在進行中,即使它們實際上已提交或中止。 此規則很重要,因為它會導致“已提交讀取”和“可重複讀取”(或“可序列化”)之間的行為差異。 我們將在以下各節中反覆引用此規則。
在本節的其餘部分,事務管理器和事務將使用圖 1 中的特定方案9 來描述。
事務管理器始終儲存有關當前正在執行的事務的資訊。 假設三個事務乙個接乙個地啟動,事務 A 和事務 B 在提交讀取時被隔離,事務 c 在可重複讀取時被隔離。
t1:
事務 A 啟動並執行第乙個 select 命令。 當執行第乙個命令時,事務 A 請求當前時刻的 txid 和快照。 在這種情況下,事務管理器分配 txid 200 並返回 “200:200:” 的事務快照。
t2:
事務 B 啟動並執行第乙個 select 命令。 事務管理器分配 txid 201 並返回事務快照 “200:200:”,因為事務 A (txid 200) 正在進行中。 因此,事務 A 在事務 b 中不可見。
t3:
事務 C 啟動並執行第乙個 select 命令。 事務管理器分配 txid 202 並返回事務的快照'200:200:',因此事務 A 和事務 B 在事務 c 中不可見。
t4:
事務 A 已提交。 事務管理器刪除有關事務的資訊。
t5:
事務 B 和事務 C 執行各自的 select 命令。
事務 B 需要事務快照,因為它處於“已提交讀”級別。 在這種情況下,事務 B 將獲得乙個新的快照“201:201:”,因為事務 A (txid 200) 已提交。 因此,事務 A 不再對事務 B 可見。 事務 C 不需要事務快照,因為它處於可重複讀取級別,並使用獲取的快照,即“200:200:”。 因此,事務 A 對事務 c 仍然不可見。
可見性檢查規則是一組規則,它們使用元組的 t xmin 和 t xmax、clog 以及拍攝的事務快照來確定每個元組是可見還是不可見。 這些規則太複雜了,無法詳細解釋。 因此,本文件顯示了後續描述所需的最低規則。 下面,我們省略了與子事務相關的規則,並忽略了關於 t ctid 的討論,即我們不考慮在乙個事務中更新元組超過兩次。
選擇的規則數量為十個,可分為三種情況。
t xmin 狀態為 aborted 的元組始終不可見(規則 1),因為插入元組的事務已中止。
/* t_xmin status == aborted */rule 1: if t_xmin status is 'aborted' then return 'invisible' end if
此規則顯式表示為以下數學表示式。
• rule 1: if status(t_xmin) = aborted ⇒ invisible
t xmin 狀態為 In Progress 的元組本質上是不可見的(規則 3 和 4),但有一種情況除外。
/* t_xmin status == in_progress */if t_xmin status is 'in_progress' thenif t_xmin = current_txid thenrule 2: if t_xmax = invalid thenreturn 'visible'rule 3: else /* this tuple has been deleted or updated by the current transaction itself. */return 'invisible'end ifrule 4: else /* t_xmin ≠ current_txid */return 'invisible'end ifend if
如果這個元組是由另乙個事務插入的,並且 t xmin 的狀態正在進行中,那麼這個元組顯然是不可見的(規則 4)。
如果 t xmin 等於當前 txid(即元組是由當前事務插入的),並且 t xmax 不是無效的,則該元組是不可見的,因為它已被當前事務更新或刪除(規則 3)。
異常是指元組入到當前事務中,並且 t xmax 無效。 在這種情況下,元組必須對當前事務可見(規則 2),因為此元組是當前事務本身插入的元組。
rule 2: if status(t_xmin) = in_progress ∧ t_xmin = current_txid ∧ t_xmax = invaild ⇒ visible
rule 3: if status(t_xmin) = in_progress ∧ t_xmin = current_txid ∧ t_xmax ≠ invaild ⇒ invisible
rule 4: if status(t_xmin) = in_progress ∧ t_xmin ≠ current_txid ⇒ invisible
t xmin 狀態為 committed 的元組是可見的(規則 6、8 和 9),但以下三個例外。
/* t_xmin status == committed */if t_xmin status is 'committed' thenrule 5: if t_xmin is active in the obtained transaction snapshot thenreturn 'invisible'rule 6: else if t_xmax = invalid or status of t_xmax is 'aborted' thenreturn 'visible'else if t_xmax status is 'in_progress' thenrule 7: if t_xmax = current_txid thenreturn 'invisible'rule 8: else /* t_xmax ≠ current_txid */return 'visible'end ifelse if t_xmax status is 'committed' thenrule 9: if t_xmax is active in the obtained transaction snapshot thenreturn 'visible'rule 10: elsereturn 'invisible'end ifend ifend if
規則 6 很明確,因為 t xmax 無效或中止。 這三種例外情況以及細則第8條和第9條如下所述
第乙個例外是當 t xmin 在所獲取交易的快照中處於活動狀態時(規則 5)。 在這種情況下,元組是不可見的,因為 t xmin 應該被考慮在進行中。
第二個例外是 t xmax 是當前 txid(規則 7)。 在這種情況下,與規則 3 一樣,元組不可見,因為它已被事務本身更新或刪除。
相反,如果 t xmax 的狀態為“正在進行”,並且 t xmax 不是當前的 txid(規則 8),則元組是可見的,因為它尚未被刪除。
第三個例外情況是 t xmax 的狀態為 commited,而 t xmax 在拍攝的事務快照中處於非活動狀態(規則 10)。 在這種情況下,元組不可見,因為它已被其他事務更新或刪除。
相反,如果 t xmax 的狀態為 committed,但 t xmax 在事務的快照中處於活動狀態(規則 9),則元組是可見的,因為 t xmax 應被視為正在進行中。
rule 5: if status(t_xmin) = committed ∧ snapshot(t_xmin) = active ⇒ invisible
rule 6: if status(t_xmin) = committed ∧ t_xmax = invalid ∨ status(t_xmax) = aborted) ⇒visible
rule 7: if status(t_xmin) = committed ∧ status(t_xmax) = in_progress ∧ t_xmax = current_txid ⇒ invisible
rule 8: if status(t_xmin) = committed ∧ status(t_xmax) = in_progress ∧ t_xmax ≠ current_txid ⇒ visible
rule 9: if status(t_xmin) = committed ∧ status(t_xmax) = committed ∧ snapshot(t_xmax) = active ⇒ visible
rule 10: if status(t_xmin) = committed ∧ status(t_xmax) = committed ∧ snapshot(t_xmax) ≠active ⇒ invisible
本節介紹PostgreSQL如何執行可見性檢查,這是為給定事務選擇適當版本的堆元組的過程。 本節還介紹了 PostgreSQL 如何防止 ANSI SQL-92 標準中定義的異常:髒讀、可重複讀和幻像讀。
圖1圖 10 顯示了描述可見性檢查的方案。
圖110。描述可見性檢查的方案。
在圖 1 中在 10 所示的場景中,SQL 命令按以下順序執行:
T1:開始交易(txid 200)。
T2:開始交易(txid 201)。
t3:對 txid 200 和 201 執行 select 命令。
T4:對 txid 200 執行 update 命令,T5:對 txid 200 和 201 執行 select 命令。
t6:提交 txid 200
T7:執行TXID 201的select命令。
為了簡化描述,我們假設只有兩個事務,txid 200 和 201。 txid 200 的隔離級別為“讀提交”,txid 201 的隔離級別為“讀提交”或“可重複讀取”。
我們如何選擇命令對每個元組執行可見性檢查。
select commands of t3:
在 t3 處,表 tbl 中只有元組 1,並且根據規則 6 可見。 因此,兩個事務中的 select 命令都返回“jekyll”。
rule6(tuple_1) ⇒status(t_xmin:199) = committed ∧ t_xmax = invalid ⇒ visible
testdb=# --txid 200testdb=# select * from tbl;name---jekyll(1 row)testdb=# --txid 201testdb=# select * from tbl;name---jekyll(1 row)
select commands of t5:首先,讓我們探討一下 txid 200 執行的 select 命令。 根據規則 7,元組 1 不可見,而在規則 2 下,元組 2 可見。 因此,select 命令返回“hyde”。
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
testdb=# --txid 200testdb=# select * from tbl;name---hyde(1 row)
另一方面,在 txid 201 執行的 select 命令中,元組 1 根據規則 8 是可見的,而元組 2 根據規則 4 是不可見的。 因此,select 命令返回“jekyll”。
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
testdb=# --txid 201testdb=# select * from tbl;name---jekyll(1 row)
如果更新的元組在提交之前從其他事務中可見,則稱為髒讀,也稱為 wr 衝突。 但是,如上所示,在PostgreSQL的任何隔離級別下都不會發生髒讀。
select command of t7:
下面描述了 t7 的 select 命令在兩個隔離級別上的行為。
當 txid 201 處於讀取提交級別時,txid 200 被視為已提交,因為事務快照為“201:201:”。 因此,在規則 10 下,元組 1 不可見,而在規則 6 下,元組 2 可見。 select 命令返回“hyde”。
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
testdb=# --txid 201 (read committed)testdb=# select * from tbl;name---hyde
請注意,在提交 txid 200 之前和之後執行的 select 命令的結果是不同的。 這通常稱為不可重複讀取。
相反,當 txid 201 處於可重複讀取級別時,txid 200 必須被視為正在進行中,因為事務快照為“200:200:”。 因此,在規則 9 下,元組 1 是可見的,而在規則 5 下,元組 2 是不可見的。 select 命令返回“jekyll”。 請注意,不可重複讀取不會在可重複讀取(和可序列化)級別發生。
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、
testdb=# --txid 201 (repeatable read)testdb=# select * from tbl;name---jekyll(1 row)
ANSI SQL-92 標準中定義的可重複讀取允許虛擬讀取。 但是,PostgreSQL 的實現不允許它們。 原則上,SI 不允許幻象讀數。
假設兩個事務,即 tx a 和 tx b,同時執行。 它們的隔離級別為“已提交讀取”和“可重複讀取”,其 txid 分別為 100 和 101。 首先,tx a 插入乙個元組。 然後,它被承諾了。 插入的元組的 t xmin 為 100。 接下來,tx b 執行 select 命令; 但是,根據規則 5,TX A 插入的元組是不可見的。 因此,不會發生幻象讀數。
rule5(new tuple): status(t_xmin:100) = committed ∧ snapshot(t_xmin:100) = active ⇒ invisible
交易 A
testdb=# --tx_a: txid 100testdb=# start transactiontestdb-# isolation level read committed;start transactiontestdb=# insert tbl(id, data)values (1,'phantom');insert 1testdb=# commit;commit
交易 B
testdb=# --tx_b: txid 101testdb=# start transactiontestdb-# isolation level repeatable read;start transactiontestdb=# select txid_current();txid_current---101(1 row)10testdb=# select * from tbl where id=1;id | data---0 rows)