您好,登錄后才能下訂單哦!
如前所述,PostgreSQL使用一種稱為快照隔離Snapshot Isolation (SI)實現并發控制,下面簡單介紹PostgreSQL中與之相關的快照和隔離級別等基本概念。
注意:
1.簡單起見,本節暫不討論事務回滾的情況.
2.如無說明,默認的事務隔離級別為READ COMMITTED
事務快照(簡稱Snapshot)存儲所有事務在某個時間點是否處于活動狀態(正在進行或尚未啟動)的相關信息.
通過txid_current_snapshot()函數可以獲取當前事務的快照信息.
testdb=# select txid_current_snapshot();
txid_current_snapshot
-----------------------
2315:2315:
(1 row)
快照的格式如下為xmin : xmax : xip_list
其中:
xmin : 最早仍活躍的事務txid,早于此txid的事務要么被提交并可見,要么回滾并廢棄。
xmax :
ShmemVariableCache->latestCompletedXid + 1,即最后已完結事務(COMMITTED/ABORTED)的txid + 1
。在”拍攝”快照時,所有大于或等于此txid的事務尚未啟動,因此不可見。
xip_list : 在”拍攝”快照時處于活動狀態的事務txid。該列表包含xmin和xmax之間的活動txid。
總結一下,簡單來說,對于給定的事務txid:
txid ∈ [2,xmin),是過去的事務,對此快照均可見;
txid ∈ [xmin,xmax),txid仍處于IN_PROGRESS狀態,則不可見;COMMITED狀態,則可見;ABORTED的事務,亦不可見;
txid ∈ [xmax,∞),是未來的事務,對此快照均不可見;
啟動三個客戶端(psql),執行begin啟動事務:
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的事務可見,大于等于2327的不可見.
session 2創建數據表,插入數據,提交,重新開啟事務,插入數據.
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的快照信息變為2327:2329:,即小于2327的事務可見,大于等于2329的事務不可見.
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的快照信息變為2327:2329:2327,即小于2327的事務可見,大于等于2329的事務不可見,2327事務進行中,亦不可見.
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創建數據表并插入數據
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查詢數據表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(); --> 一旦出錯,就要回滾事務了
ERROR: current transaction is aborted, commands ignored until end of transaction block
11:50:28 (xdb@[local]:5432)testdb=#! commit; --> 雖然是commit,按提示信息,其實是rollback.
ROLLBACK
報錯,提示關系不存在.
注意:事務處理過程中,如出錯,Oracle仍可提交先前成功執行的操作,但PG要求回滾事務
在上述案例中,在同一個事務中,執行新的SQL時事務快照是會變化的,這是因為默認的隔離級別READ COMMITTED的機制使然,如隔離級別為REPEATABLE READ則事務快照只會獲取一次并記錄獲取快照時事務的狀態.
在PostgreSQL中,有四種隔離級別,分別是READ UNCOMMITTED/READ COMMITTED/REPEATABLE READ/SERIALIZABLE.
其中READ UNCOMMITTED與READ COMMITTED一致,沒有區別;SERIALIZABLE級別較少使用,這里不作敘述.
READ COMMITTED
沿用第1小節的session 1/2.
session 2重新啟動事務
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提交事務
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數據表
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插入數據,提交事務,這時候新插入的數據對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 --> 新插入的數據2不可見
(1 row)
12:13:58 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
txid_current_snapshot
-----------------------
2329:2332:2329
(1 row)
session 2的事務快照只會獲取一次.
獲取事務快照的邏輯以及如何確定heap tuple是否可見的邏輯后續在源碼解讀中將作具體解析.
Concurrency Control
打錯字,PG也要求回滾事務?
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
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。