溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

PostgreSQL DBA(23) - MVCC#3(事務(wù)快照和隔離級別)

發(fā)布時間:2020-08-10 08:47:25 來源:ITPUB博客 閱讀:153 作者:husthxd 欄目:關(guān)系型數(shù)據(jù)庫

如前所述,PostgreSQL使用一種稱為快照隔離Snapshot Isolation (SI)實現(xiàn)并發(fā)控制,下面簡單介紹PostgreSQL中與之相關(guān)的快照和隔離級別等基本概念。
注意:
1.簡單起見,本節(jié)暫不討論事務(wù)回滾的情況.
2.如無說明,默認的事務(wù)隔離級別為READ COMMITTED

一、事務(wù)快照

事務(wù)快照(簡稱Snapshot)存儲所有事務(wù)在某個時間點是否處于活動狀態(tài)(正在進行或尚未啟動)的相關(guān)信息.
通過txid_current_snapshot()函數(shù)可以獲取當前事務(wù)的快照信息.


testdb=# select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2315:2315:
(1 row)

快照的格式如下為xmin : xmax : xip_list
其中:
xmin : 最早仍活躍的事務(wù)txid,早于此txid的事務(wù)要么被提交并可見,要么回滾并廢棄。
xmax : ShmemVariableCache->latestCompletedXid + 1,即最后已完結(jié)事務(wù)(COMMITTED/ABORTED)的txid + 1 。在”拍攝”快照時,所有大于或等于此txid的事務(wù)尚未啟動,因此不可見。
xip_list : 在”拍攝”快照時處于活動狀態(tài)的事務(wù)txid。該列表包含xmin和xmax之間的活動txid。
總結(jié)一下,簡單來說,對于給定的事務(wù)txid:
txid ∈ [2,xmin),是過去的事務(wù),對此快照均可見;
txid ∈ [xmin,xmax),txid仍處于IN_PROGRESS狀態(tài),則不可見;COMMITED狀態(tài),則可見;ABORTED的事務(wù),亦不可見;
txid ∈ [xmax,∞),是未來的事務(wù),對此快照均不可見;

啟動三個客戶端(psql),執(zhí)行begin啟動事務(wù):
session 1


11:36:04 (xdb@[local]:5432)testdb=# begin;
BEGIN
11:36:09 (xdb@[local]:5432)testdb=#* select 1;
 ?column? 
----------
        1
(1 row)
11:36:09 (xdb@[local]:5432)testdb=#* select txid_current();
 txid_current 
--------------
         2327
(1 row)
11:36:09 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2327:
(1 row)

session 2


11:36:02 (xdb@[local]:5432)testdb=# begin;
BEGIN
11:36:17 (xdb@[local]:5432)testdb=#* select 1;
 ?column? 
----------
        1
(1 row)
11:36:17 (xdb@[local]:5432)testdb=#* select txid_current();
 txid_current 
--------------
         2328
(1 row)
11:36:17 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2327:
(1 row)

session 3


11:35:59 (xdb@[local]:5432)testdb=# begin;
BEGIN
11:36:18 (xdb@[local]:5432)testdb=#* select 1;
 ?column? 
----------
        1
(1 row)
11:36:18 (xdb@[local]:5432)testdb=#* select txid_current();
 txid_current 
--------------
         2329
(1 row)
11:36:18 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2327:
(1 row)

session 1/2/3的txid分別是2327/2328/2329,快照均為2327:2327:,即小于2327的事務(wù)可見,大于等于2327的不可見.

session 2創(chuàng)建數(shù)據(jù)表,插入數(shù)據(jù),提交,重新開啟事務(wù),插入數(shù)據(jù).


11:36:17 (xdb@[local]:5432)testdb=#* 
11:37:24 (xdb@[local]:5432)testdb=#* create table t_session2 (id int);
CREATE TABLE
11:37:28 (xdb@[local]:5432)testdb=#* insert into t_session2 values(1);
INSERT 0 1
11:37:28 (xdb@[local]:5432)testdb=#* commit;
COMMIT
11:37:28 (xdb@[local]:5432)testdb=# begin;
BEGIN
11:38:25 (xdb@[local]:5432)testdb=#* insert into t_session2 values(2);
INSERT 0 1
11:38:31 (xdb@[local]:5432)testdb=#*

查看session 1/2/3的快照信息變化.
session 1


11:38:03 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2329:
(1 row)

session 1的快照信息變?yōu)?327:2329:,即小于2327的事務(wù)可見,大于等于2329的事務(wù)不可見.
session 2


11:39:02 (xdb@[local]:5432)testdb=#* select txid_current();
 txid_current 
--------------
         2330
(1 row)
11:39:14 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2329:2327
(1 row)

session 2的快照信息變?yōu)?327:2329:2327,即小于2327的事務(wù)可見,大于等于2329的事務(wù)不可見,2327事務(wù)進行中,亦不可見.

session 3


11:36:18 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2329:2327
(1 row)
11:39:57 (xdb@[local]:5432)testdb=#*

session 3的快照信息與session 2一致.

session 1創(chuàng)建數(shù)據(jù)表并插入數(shù)據(jù)


11:40:36 (xdb@[local]:5432)testdb=#* create table t_session1 (id int);
CREATE TABLE
11:45:10 (xdb@[local]:5432)testdb=#* insert into t_session1 values(1);
INSERT 0 1
11:45:19 (xdb@[local]:5432)testdb=#*

session 2查詢數(shù)據(jù)表t_session1


11:39:17 (xdb@[local]:5432)testdb=#* select * from t_session1;
ERROR:  relation "t_session1" does not exist
LINE 1: select * from t_session1;
11:49:58 (xdb@[local]:5432)testdb=#! select txid_current_snapshot(); --> 一旦出錯,就要回滾事務(wù)了
ERROR:  current transaction is aborted, commands ignored until end of transaction block
11:50:28 (xdb@[local]:5432)testdb=#! commit; --> 雖然是commit,按提示信息,其實是rollback.
ROLLBACK

報錯,提示關(guān)系不存在.
注意:事務(wù)處理過程中,如出錯,Oracle仍可提交先前成功執(zhí)行的操作,但PG要求回滾事務(wù)

在上述案例中,在同一個事務(wù)中,執(zhí)行新的SQL時事務(wù)快照是會變化的,這是因為默認的隔離級別READ COMMITTED的機制使然,如隔離級別為REPEATABLE READ則事務(wù)快照只會獲取一次并記錄獲取快照時事務(wù)的狀態(tài).

二、隔離級別

在PostgreSQL中,有四種隔離級別,分別是READ UNCOMMITTED/READ COMMITTED/REPEATABLE READ/SERIALIZABLE.
其中READ UNCOMMITTED與READ COMMITTED一致,沒有區(qū)別;SERIALIZABLE級別較少使用,這里不作敘述.
READ COMMITTED
沿用第1小節(jié)的session 1/2.
session 2重新啟動事務(wù)


12:06:17 (xdb@[local]:5432)testdb=#*  select txid_current();
 txid_current 
--------------
         2331
(1 row)
12:06:23 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2331:2327,2329
(1 row)

session 1提交事務(wù)


11:40:36 (xdb@[local]:5432)testdb=#* create table t_session1 (id int);
CREATE TABLE
11:45:10 (xdb@[local]:5432)testdb=#* insert into t_session1 values(1);
INSERT 0 1
11:45:19 (xdb@[local]:5432)testdb=#* commit;
COMMIT
12:07:21 (xdb@[local]:5432)testdb=#

這時候session 2可讀取session 1中的t_session1數(shù)據(jù)表


12:08:21 (xdb@[local]:5432)testdb=#* select * from t_session1;
 id 
----
  1
(1 row)

REPEATABLE READ
session 1,隔離級別為READ COMMITTED


12:12:14 (xdb@[local]:5432)testdb=# START TRANSACTION ISOLATION LEVEL READ COMMITTED; 
START TRANSACTION

session 2,隔離級別為REPEATABLE READ


12:12:41 (xdb@[local]:5432)testdb=# START TRANSACTION ISOLATION LEVEL REPEATABLE READ; 
START TRANSACTION
12:12:43 (xdb@[local]:5432)testdb=#* select * from t_session1;
 id 
----
  1
(1 row)

仍使用上述場景,session 1插入數(shù)據(jù),提交事務(wù),這時候新插入的數(shù)據(jù)對session 2仍不可見.
session 1


12:12:18 (xdb@[local]:5432)testdb=#* insert into t_session1 values(2);
INSERT 0 1
12:13:22 (xdb@[local]:5432)testdb=#* commit;
COMMIT
12:13:25 (xdb@[local]:5432)testdb=#

session 2


12:13:38 (xdb@[local]:5432)testdb=#* select * from t_session1;
 id 
----
  1 --> 新插入的數(shù)據(jù)2不可見
(1 row)
12:13:58 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2329:2332:2329
(1 row)

session 2的事務(wù)快照只會獲取一次.
獲取事務(wù)快照的邏輯以及如何確定heap tuple是否可見的邏輯后續(xù)在源碼解讀中將作具體解析.

三、參考資料

Concurrency Control

四、號外

打錯字,PG也要求回滾事務(wù)?


12:06:25 (xdb@[local]:5432)testdb=#* selet * from t_session1;
ERROR:  syntax error at or near "selet"
LINE 1: selet * from t_session1;
        ^
12:07:56 (xdb@[local]:5432)testdb=#! select * from t_session1;
ERROR:  current transaction is aborted, commands ignored until end of transaction block
12:08:02 (xdb@[local]:5432)testdb=#! commit;
ROLLBACK

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI