摘要:深入解析系列之并發(fā)控制與事務(wù)機制并發(fā)控制旨在針對數(shù)據(jù)庫中對事務(wù)并行的場景,保證中的一致性與隔離。啟動并執(zhí)行第一個命令。事務(wù)管理器分配,并返回事務(wù)快照,因為正在進行中。意味著該行由另一個并發(fā)事務(wù)更新,并且其事務(wù)尚未終止。
深入解析 PostgreSQL 系列整理自 The Internals of PostgreSQL 等系列文章,從碎片化地閱讀到體系化地學(xué)習(xí),感覺對數(shù)據(jù)庫有了更深入地了解;觸類旁通,相互印證,也是有利于掌握 MySQL 等其他的關(guān)系型數(shù)據(jù)庫或者 NoSQL 數(shù)據(jù)庫。深入解析 PostgreSQL 系列之并發(fā)控制與事務(wù)機制
并發(fā)控制旨在針對數(shù)據(jù)庫中對事務(wù)并行的場景,保證 ACID 中的一致性(Consistency)與隔離(Isolation)。數(shù)據(jù)庫技術(shù)中主流的三種并發(fā)控制技術(shù)分別是: Multi-version Concurrency Control (MVCC), Strict Two-Phase Locking (S2PL), 以及 Optimistic Concurrency Control (OCC),每種技術(shù)也都有很多的變種。在 MVCC 中,每次寫操作都會在舊的版本之上創(chuàng)建新的版本,并且會保留舊的版本。當(dāng)某個事務(wù)需要讀取數(shù)據(jù)時,數(shù)據(jù)庫系統(tǒng)會從所有的版本中選取出符合該事務(wù)隔離級別要求的版本。MVCC 的最大優(yōu)勢在于讀并不會阻塞寫,寫也不會阻塞讀;而像 S2PL 這樣的系統(tǒng),寫事務(wù)會事先獲取到排他鎖,從而會阻塞讀事務(wù)。
PostgreSQL 以及 Oracle 等 RDBMS 實際使用了所謂的 Snapshot Isolation(SI)這個 MVCC 技術(shù)的變種。Oracle 引入了額外的 Rollback Segments,當(dāng)寫入新的數(shù)據(jù)時,老版本的數(shù)據(jù)會被寫入到 Rollback Segment 中,隨后再被覆寫到實際的數(shù)據(jù)塊。PostgreSQL 則是使用了相對簡單的實現(xiàn)方式,新的數(shù)據(jù)對象會被直接插入到關(guān)聯(lián)的 Table Page 中;而在讀取表數(shù)據(jù)的時候,PostgreSQL 會通過可見性檢測規(guī)則(Visibility Check Rules)來選擇合適的版本。
SI 能夠避免 ANSI SQL-92 標(biāo)準(zhǔn)中定義的三個反常現(xiàn)象:臟讀(Dirty Reads),不可重復(fù)讀(Non-Repeatable Reads)以及幻讀(Phantom Reads);在 9.1 版本后引入的 Serializable Snapshot Isolation(SSI)則能夠提供真正的順序讀寫的能力。
Isolation Level | Dirty Reads | Non-repeatable Read | Phantom Read | Serialization Anomaly |
---|---|---|---|---|
READ COMMITTED | Not possible | Possible | Possible | Possible |
REPEATABLE READ | Not possible | Not possible | Not possible in PG; See Section 5.7.2. (Possible in ANSI SQL) | Possible |
SERIALIZABLE | Not possible | Not possible | Not possible | Not possible |
當(dāng)某個事務(wù)開啟時,PostgreSQL 內(nèi)置的 Transaction Manager 會為它分配唯一的 Transaction ID(txid);txid 是 32 位無類型整型值,可以通過 txid_current() 函數(shù)來獲取當(dāng)前的 txid:
testdb=# BEGIN; BEGIN testdb=# SELECT txid_current(); txid_current -------------- 100 (1 row)
PostgreSQL 還保留了三個關(guān)鍵 txid 值作特殊標(biāo)記:0 表示無效的 txid,1 表示啟動時的 txid,僅在 Database Cluster 啟動時使用;2 代表了被凍結(jié)的(Frozen)txid,用于在序列化事務(wù)時候使用。PostgreSQL 選擇數(shù)值類型作為 txid,也是為了方便進行比較;對于 txid 值為 100 的事務(wù)而言,所有小于 100 的事務(wù)是發(fā)生在過去的,可見的;而所有大于 100 的事務(wù),是發(fā)生在未來,即不可見的。
鑒于實際系統(tǒng)中的 txid 數(shù)目的需要可能會超過最大值,PostgreSQL 實際是將這些 txid 作為環(huán)來看待。
HeapTupleHeaderDataTable Pages 中的 Heap Tuples 往往包含三個部分:HeapTupleHeaderData 結(jié)構(gòu),NULL bitmap 以及用戶數(shù)據(jù)。
其中 HeapTupleHeaderData 與事物處理強相關(guān)的屬性有:
(TransactionId)t_xmin: 存放插入該 Tuple 時的 txid
(TransactionId)t_xmax: 存放刪除或者更新該 Tuple 時的 txid,如果還沒更新或者刪除,那么置 0,表示無效
(CommandId)t_cid: 存放 Command ID,即 創(chuàng)建該 Tuple 的命令在該事務(wù)內(nèi)執(zhí)行的所有 SQL 命令中的編號;譬如 BEGIN; INSERT; INSERT; INSERT; COMMIT; 這個事務(wù),如果是首個 INSERT 命令創(chuàng)建的 Tuple,那么其 t_cid 值為 0,第二個就是 1
(ItemPointerData)t_ctid: 當(dāng)某個 Tuple 更新時,該值就指向新創(chuàng)建的 Tuple,否則指向自己
Tuple 的插入、刪除與更新如上所述,Table Pages 中的 Tuples 呈如下布局:
插入在執(zhí)行插入操作時,PostgreSQL 會直接將某個新的 Tuple 插入到目標(biāo)表的某個頁中:
假如某個 txid 為 99 的事務(wù)插入了新的 Tuple,那么該 Tuple 的頭域會被設(shè)置為如下值:
t_xmin 與創(chuàng)建該 Tuple 的事務(wù)的 txid 保持一致,即 99
t_xmax 被設(shè)置為 0,因為其還未被刪除或者更新
t_cid 被設(shè)置為 0,因為該 Tuple 是由事務(wù)中的首個 Insert 命令創(chuàng)建的
t_ctid 被設(shè)置為了 (0, 1),即指向了自己
testdb=# CREATE EXTENSION pageinspect; CREATE EXTENSION testdb=# CREATE TABLE tbl (data text); CREATE TABLE testdb=# INSERT INTO tbl VALUES("A"); INSERT 0 1 testdb=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid FROM heap_page_items(get_raw_page("tbl", 0)); tuple | t_xmin | t_xmax | t_cid | t_ctid -------+--------+--------+-------+-------- 1 | 99 | 0 | 0 | (0,1)刪除
在刪除操作中,目標(biāo) Tuple 會被先邏輯刪除,即將 t_xmax 的值設(shè)置為當(dāng)前刪除該 Tuple 的事務(wù)的 txid 值。
當(dāng)該事務(wù)被提交之后,PostgreSQL 會將該 Tuple 標(biāo)記為 Dead Tuple,并隨后在 VACUUM 處理過程中被徹底清除。
更新在更新操作時,PostgreSQL 會首先邏輯刪除最新的 Tuple,然后插入新的 Tuple:
上圖所示的行被 txid 為 99 的事務(wù)插入,被 txid 為 100 的事務(wù)連續(xù)更新兩次;在該事務(wù)提交之后,Tuple_2 與 Tuple_3 就會被標(biāo)記為 Dead Tuples。
Free Space Map當(dāng)插入某個 Heap Tuple 或者 Index Tuple 時,PostgreSQL 使用相關(guān)表的 FSM 來決定應(yīng)該選擇哪個 Page 來進行具體的插入操作。每個 FSM 都存放著表或者索引文件相關(guān)的剩余空間容量的信息,可以使用如下方式查看:
testdb=# CREATE EXTENSION pg_freespacemap; CREATE EXTENSION testdb=# SELECT *, round(100 * avail/8192 ,2) as "freespace ratio" FROM pg_freespace("accounts"); blkno | avail | 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 ....Commit Log
PostgreSQL 使用 Commit Log,亦稱 clog 來存放事務(wù)的狀態(tài);clog 存放于 Shared Memory 中,在整個事務(wù)處理的生命周期中都起到了重要的作用。PostgreSQL 定義了四種不同的事務(wù)狀態(tài):IN_PROGRESS, COMMITTED, ABORTED, 以及 SUB_COMMITTED。
Clog 有 Shared Memory 中多個 8KB 大小的頁構(gòu)成,其邏輯上表現(xiàn)為類數(shù)組結(jié)構(gòu),數(shù)組下標(biāo)即是關(guān)聯(lián)的事務(wù)的 txid,而值就是當(dāng)前事務(wù)的狀態(tài):
如果當(dāng)前的 txid 超過了當(dāng)前 clog 頁可承載的最大范圍,那么 PostgreSQL 會自動創(chuàng)建新頁。而在 PostgreSQL 停止或者 Checkpoint 進程運行的時候,clog 的數(shù)據(jù)會被持久化存儲到 pg_xact 子目錄下,以 0000,0001 依次順序命名,單個文件的最大尺寸為 256KB。而當(dāng) PostgreSQL 重啟的時候,存放在 pg_xact 目錄下的文件會被重新加載到內(nèi)存中。而隨著 PostgreSQL 的持續(xù)運行,clog 中勢必會累計很多的過時或者無用的數(shù)據(jù),Vacuum 處理過程中同樣會清除這些無用的數(shù)據(jù)。
Transaction Snapshot | 事務(wù)快照事務(wù)快照即是存放了當(dāng)前全部事務(wù)是否為激活狀態(tài)信息的數(shù)據(jù)結(jié)構(gòu),PostgreSQL 內(nèi)部將快照表示為簡單的文本結(jié)構(gòu),xmin:xmax:xip_list’;譬如 "100",其意味著所有 txid 小于或者等于 99 的事務(wù)是非激活狀態(tài),而大于等于 100 的事務(wù)是處在了激活狀態(tài)。
testdb=# SELECT txid_current_snapshot(); txid_current_snapshot ----------------------- 100:104:100,102 (1 row)
xmin: 最早的仍處在激活狀態(tài)的 txid,所有更早之前的事務(wù)要么處于被提交之后的可見態(tài),要么就是被回滾之后的假死態(tài)。
xmax: 首個至今仍未分配的事務(wù)編號,所有 txid 大于或者等于該值的事務(wù),相對于該快照歸屬的事務(wù)都是尚未發(fā)生的,因此是不可見的。
xip_list: 快照時候處于激活狀態(tài)的 txids,僅會包含在 xmin 與 xmax 之間的 txids。
以 100:104:100,102 為例,其示意圖如下所示:
事務(wù)快照主要由事務(wù)管理器(Transaction Manager)提供,在 READ COMMITTED 這個隔離級別,無論是否有 SQL 命令執(zhí)行,該事務(wù)都會被分配到某個快照;而對于 REPEATABLE READ 或者 SERIALIZABLE 隔離級別的事務(wù)而言,僅當(dāng)首個 SQL 語句被執(zhí)行的時候,才會被分配到某個事務(wù)快照用于進行可見性檢測。事務(wù)快照的意義在于,當(dāng)某個快照進行可見性判斷時,無論目標(biāo)事務(wù)是否已經(jīng)被提交或者放棄,只要他在快照中被標(biāo)記為 Active,那么其就會被當(dāng)做 IN_PROGRESS 狀態(tài)的事務(wù)來處理。
事務(wù)管理器始終保存有關(guān)當(dāng)前運行的事務(wù)的信息。假設(shè)三個事務(wù)一個接一個地開始,并且 Transaction_A 和 Transaction_B 的隔離級別是 READ COMMITTED,Transaction_C 的隔離級別是 REPEATABLE READ。
T1:
Transaction_A 啟動并執(zhí)行第一個 SELECT 命令。執(zhí)行第一個命令時,Transaction_A 請求此刻的 txid 和快照。在這種情況下,事務(wù)管理器分配 txid 200,并返回事務(wù)快照"200:200:"。
T2:
Transaction_B 啟動并執(zhí)行第一個 SELECT 命令。事務(wù)管理器分配 txid 201,并返回事務(wù)快照"200:200:",因為 Transaction_A(txid 200)正在進行中。因此,無法從 Transaction_B 中看到 Transaction_A。
T3:
Transaction_C 啟動并執(zhí)行第一個 SELECT 命令。事務(wù)管理器分配 txid 202,并返回事務(wù)快照"200:200:",因此,Transaction_A 和 Transaction_B 不能從 Transaction_C 中看到。
T4:
Transaction_A 已提交。事務(wù)管理器刪除有關(guān)此事務(wù)的信息。
T5:
Transaction_B 和 Transaction_C 執(zhí)行各自的 SELECT 命令。
Transaction_B 需要事務(wù)快照,因為它處于 READ COMMITTED 級別。在這種情況下,Transaction_B 獲取新快照"201:201:",因為 Transaction_A(txid 200)已提交。因此,Transaction_B 不再是 Transaction_B 中不可見的。
Transaction_C 不需要事務(wù)快照,因為它處于 REPEATABLE READ 級別并使用獲得的快照,即"200:200:"。因此,Transaction_A 仍然是 Transaction_C 不可見的。
Visibility Check | 可見性檢測 Rules | 可見性檢測規(guī)則可見性檢測的規(guī)則用于根據(jù) Tuple 的 t_xmin 與 t_xmax,clog 以及自身分配到的事務(wù)快照來決定某個 Tuple 相對于某個事務(wù)是否可見。
t_xmin 對應(yīng)事務(wù)的狀態(tài)為 ABORTED當(dāng)某個 Tuple 的 t_xmin 值對應(yīng)的事務(wù)的狀態(tài)為 ABORTED 時候,該 Tuple 永遠是不可見的:
/* t_xmin status = ABORTED */ // Rule 1: If Status(t_xmin) = ABORTED ? Invisible Rule 1: IF t_xmin status is "ABORTED" THEN RETURN "Invisible" END IFt_xmin 對應(yīng)事務(wù)的狀態(tài)為 IN_PROGRESS
對于非插入該 Tuple 的事務(wù)之外的其他事務(wù)關(guān)聯(lián)的 Tuple 而言,該 Tuple 永遠是不可見的;僅對于與該 Tuple 同屬一事務(wù)的 Tuple 可見(此時該 Tuple 未被刪除或者更新的)。
/* t_xmin status = IN_PROGRESS */ IF t_xmin status is "IN_PROGRESS" THEN IF t_xmin = current_txid THEN // Rule 2: If Status(t_xmin) = IN_PROGRESS ∧ t_xmin = current_txid ∧ t_xmax = INVAILD ? Visible Rule 2: IF t_xmax = INVALID THEN RETURN "Visible" // Rule 3: If Status(t_xmin) = IN_PROGRESS ∧ t_xmin = current_txid ∧ t_xmax ≠ INVAILD ? Invisible Rule 3: ELSE /* this tuple has been deleted or updated by the current transaction itself. */ RETURN "Invisible" END IF // Rule 4: If Status(t_xmin) = IN_PROGRESS ∧ t_xmin ≠ current_txid ? Invisible Rule 4: ELSE /* t_xmin ≠ current_txid */ RETURN "Invisible" END IF END IFt_xmin 對應(yīng)事務(wù)的狀態(tài)為 COMMITTED
此時該 Tuple 在大部分情況下都是可見的,除了該 Tuple 被更新或者刪除。
/* t_xmin status = COMMITTED */ IF t_xmin status is "COMMITTED" THEN // If Status(t_xmin) = COMMITTED ∧ Snapshot(t_xmin) = active ? Invisible Rule 5: IF t_xmin is active in the obtained transaction snapshot THEN RETURN "Invisible" // If Status(t_xmin) = COMMITTED ∧ (t_xmax = INVALID ∨ Status(t_xmax) = ABORTED) ? Visible 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 // If Status(t_xmin) = COMMITTED ∧ Status(t_xmax) = IN_PROGRESS ∧ t_xmax = current_txid ? Invisible Rule 7: IF t_xmax = current_txid THEN RETURN "Invisible" // If Status(t_xmin) = COMMITTED ∧ Status(t_xmax) = IN_PROGRESS ∧ t_xmax ≠ current_txid ? Visible Rule 8: ELSE /* t_xmax ≠ current_txid */ RETURN "Visible" END IF ELSE IF t_xmax status is "COMMITTED" THEN // If Status(t_xmin) = COMMITTED ∧ Status(t_xmax) = COMMITTED ∧ Snapshot(t_xmax) = active ? Visible Rule 9: IF t_xmax is active in the obtained transaction snapshot THEN RETURN "Visible" // If Status(t_xmin) = COMMITTED ∧ Status(t_xmax) = COMMITTED ∧ Snapshot(t_xmax) ≠ active ? Invisible Rule 10: ELSE RETURN "Invisible" END IF END IF END IF可見性檢測流程
以簡單的雙事務(wù)更新與查詢?yōu)槔?/p>
上圖中 txid 200 的事務(wù)的隔離級別是 READ COMMITED,txid 201 的隔離級別為 READ COMMITED 或者 REPEATABLE READ。
當(dāng)在 T3 時刻執(zhí)行 SELECT 命令時:
根據(jù) Rule 6,此時僅有 Tuple_1 是處于可見狀態(tài):
# Rule6(Tuple_1) ? Status(t_xmin:199) = COMMITTED ∧ t_xmax = INVALID ? Visible testdb=# -- txid 200 testdb=# SELECT * FROM tbl; name -------- Jekyll (1 row) testdb=# -- txid 201 testdb=# SELECT * FROM tbl; name -------- Jekyll (1 row)
當(dāng)在 T5 時刻執(zhí)行 SELECT 命令時:
對于 txid 200 的事務(wù)而言,根據(jù) Rule 7 與 Rule 2 可知,Tuple_1 可見而 Tuple_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 testdb=# -- txid 200 testdb=# SELECT * FROM tbl; name ------ Hyde (1 row)
而對于 txid 201 的事務(wù)而言,Tuple_1 是可見的,Tuple_2 是不可見的:
# 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 201 testdb=# SELECT * FROM tbl; name -------- Jekyll (1 row)
當(dāng)在 T7 時刻執(zhí)行 SELECT 命令時:
如果此時 txid 201 的事務(wù)處于 READ COMMITED 的隔離級別,那么 txid 200 會被當(dāng)做 COMMITTED 來處理,因為此時獲取到的事務(wù)快照是 201:201:,因此 Tuple_1 是不可見的,而 Tuple_2 是可見的:
# 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 (1 row)
如果此時 txid 201 的事務(wù)處于 REPEATABLE READ 的隔離級別,此時獲取到的事務(wù)快照還是 200:200:,那么 txid 200 的事務(wù)必須被當(dāng)做 IN_PROGRESS 狀態(tài)來處理;因此此時 Tuple_1 是可見的,而 Tuple_2 是不可見的:
# 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)Preventing Lost Updates | 避免更新丟失
所謂的 更新丟失(Lost Update),也就是寫沖突(ww-conflict),其出現(xiàn)在兩個事務(wù)同時更新相同的行;在 PostgreSQL 中,REPEATABLE READ 與 SERIALIZABLE 這兩個級別都需要規(guī)避這種異常現(xiàn)象。
(1) FOR each row that will be updated by this UPDATE command (2) WHILE true /* The First Block */ (3) IF the target row is being updated THEN (4) WAIT for the termination of the transaction that updated the target row (5) IF (the status of the terminated transaction is COMMITTED) AND (the isolation level of this transaction is REPEATABLE READ or SERIALIZABLE) THEN (6) ABORT this transaction /* First-Updater-Win */ ELSE (7) GOTO step (2) END IF /* The Second Block */ (8) ELSE IF the target row has been updated by another concurrent transaction THEN (9) IF (the isolation level of this transaction is READ COMMITTED THEN (10) UPDATE the target row ELSE (11) ABORT this transaction /* First-Updater-Win */ END IF /* The Third Block */ ELSE /* The target row is not yet modified or has been updated by a terminated transaction. */ (12) UPDATE the target row END IF END WHILE END FOR
在上述流程中,UPDATE 命令會遍歷每個待更新行,當(dāng)發(fā)現(xiàn)該行正在被其他事務(wù)更新時進入等待狀態(tài)直到該行被解除鎖定。如果該行已經(jīng)被更新,并且隔離級別為 REPEATABLE 或者 SERIALIZABLE,則放棄更新。
Being updated 意味著該行由另一個并發(fā)事務(wù)更新,并且其事務(wù)尚未終止。因為 PostgreSQL 的 SI 使用 first-updater-win 方案, 在這種情況下,當(dāng)前事務(wù)必須等待更新目標(biāo)行的事務(wù)的終止。假設(shè)事務(wù) Tx_A 和 Tx_B 同時運行,并且 Tx_B 嘗試更新行;但是 Tx_A 已更新它并且仍在進行中,Tx_B 等待 Tx_A 的終止。在更新目標(biāo)行提交的事務(wù)之后,繼續(xù)當(dāng)前事務(wù)的更新操作。 如果當(dāng)前事務(wù)處于 READ COMMITTED 級別,則將更新目標(biāo)行; 否則 REPEATABLE READ 或 SERIALIZABLE,當(dāng)前事務(wù)立即中止以防止丟失更新。
空間整理PostgreSQL 的并發(fā)控制機制還依賴于以下的維護流程:
移除那些被標(biāo)記為 Dead 的 Tuples 與 Index Tuples
移除 clog 中過時的部分
凍結(jié)舊的 txids
更新 FSM,VM 以及其他統(tǒng)計信息
首先討論下 txid 環(huán)繞式處理的問題,假設(shè) txid 100 的事務(wù)插入了某個 Tuple_1,則該 Tuple 對應(yīng)的 t_xmin 值為 100;而后服務(wù)器又運行了許久,Tuple_1 期間并未被改變。直到 txid 為 2^31 + 101 時,對于該事務(wù)而言,其執(zhí)行 SELECT 命令時,是無法看到 Tuple_1 的 ,因為 txid 為 100 的事務(wù)相對于其是發(fā)生在未來的,由其創(chuàng)建的 Tuple 自然也就是不可見的。
為了解決這個問題,PostgreSQL 引入了所謂的 frozen txid(被凍結(jié)的 txid),并且設(shè)置了 FREEZE 進程來具體處理該問題。前文提及到 txid 2 是保留值,專門表征那些被凍結(jié)的 Tuple,這些 Tuple 永遠是非激活的、可見的。FREEZE 進程同樣由 Vacuum 進程統(tǒng)一調(diào)用,它會掃描所有的表文件,將那些與當(dāng)前 txid 差值超過 vacuum_freeze_min_age 定義的 Tuple 的 t_xmin 域設(shè)置為 2。在 9.4 版本之后,則是將 t_infomask 域中的 XMIN_FROZEN 位設(shè)置來表征該 Tuple 為凍結(jié)狀態(tài)。
延伸閱讀如果希望深入淺出 分布式系統(tǒng),分布式計算,分布式存儲,數(shù)據(jù)庫,操作系統(tǒng),虛擬化 等內(nèi)容,可以參閱 深入淺出分布式基礎(chǔ)架構(gòu), DistributedSystem CheatSheet, Database CheatSheet, Linux CheatSheet, MySQL CheatSheet, Docker CheatSheet, Flink CheatSheet, Kafka CheatSheet 等。
如果想獲取分布式系統(tǒng)、虛擬化調(diào)度、數(shù)據(jù)庫、分布式存儲、分布式計算、操作系統(tǒng)等領(lǐng)域更多資料: Docker List, Kubernetes List, Linux List, HTTP List, Distributed System List, Blockchain List, Flink List, Kafka List, Database List, MySQL List, PostgreSQL List, etc.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/17918.html
摘要:深入解析系列之并發(fā)控制與事務(wù)機制并發(fā)控制旨在針對數(shù)據(jù)庫中對事務(wù)并行的場景,保證中的一致性與隔離。啟動并執(zhí)行第一個命令。事務(wù)管理器分配,并返回事務(wù)快照,因為正在進行中。意味著該行由另一個并發(fā)事務(wù)更新,并且其事務(wù)尚未終止。 showImg(https://segmentfault.com/img/remote/1460000018081793); 深入解析 PostgreSQL 系列整理...
摘要:它是第一個把數(shù)據(jù)分布在全球范圍內(nèi)的系統(tǒng),并且支持外部一致性的分布式事務(wù)。目的是使得開發(fā)者閱讀之后,能對項目有一個初步了解,更好的參與進入的開發(fā)中。深度探索數(shù)據(jù)庫并發(fā)控制技術(shù)并發(fā)控制技術(shù)是數(shù)據(jù)庫事務(wù)處理的核心技術(shù)。 存儲過程高級篇 講解了一些存儲過程的高級特性,包括 cursor、schema、控制語句、事務(wù)等。 數(shù)據(jù)庫索引與事務(wù)管理 本篇文章為對數(shù)據(jù)庫知識的查缺補漏,從索引,事務(wù)管理,...
摘要:我的是忙碌的一年,從年初備戰(zhàn)實習(xí)春招,年三十都在死磕源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實習(xí)。因為我心理很清楚,我的目標(biāo)是阿里。所以在收到阿里之后的那晚,我重新規(guī)劃了接下來的學(xué)習(xí)計劃,將我的短期目標(biāo)更新成拿下阿里轉(zhuǎn)正。 我的2017是忙碌的一年,從年初備戰(zhàn)實習(xí)春招,年三十都在死磕JDK源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實習(xí)offer。然后五月懷著忐忑的心情開始了螞蟻金...
閱讀 3076·2021-09-22 15:20
閱讀 2599·2019-08-30 15:54
閱讀 1965·2019-08-30 14:06
閱讀 3113·2019-08-30 13:05
閱讀 2456·2019-08-29 18:36
閱讀 567·2019-08-29 15:10
閱讀 522·2019-08-29 11:17
閱讀 816·2019-08-28 18:11