有一個同學在PGFANS 羣裏面提了一個問題,在他實驗的某個操作中發現PG 和 ORACLE 使用同樣的操作流程後,得到的結果不一致。所以下面準備驗證並找到一些可以解釋的原因。
測試庫名test
測試表 test
測試數據
id age
1 20
2 22
3 24
首先我們要確認 PG 的隔離 RC的模式 ,另外我要排除一些不存在的問題
下面是整個的操作流程,由於截圖需要截圖好幾個,不利於查看,所以將其變成文字,並序列化, 每行中有執行的順序號 和 屬於的SESSION
1 test=# begin; SESSION 1 #開始SESSION 1
BEGIN
2 test=# select xmin,xmax,cmin,cmax,* from test; SESSION 1
xmin | xmax | cmin | cmax | id | age
---------+------+------+------+----+-----
2027732 | 0 | 0 | 0 | 2 | 40
2027735 | 0 | 1 | 1 | 1 | 20
(2 rows)
#查看當前的每行的事務情況
3 test=# select txid_current_if_assigned(); SESSION 1
txid_current_if_assigned
--------------------------
(1 row)
# 當前並未生成事務號
4 test=# begin; SESSION 2 # 同時啓動SESSION 2
BEGIN
test=# select * from test; SESSION 2
id | age
----+-----
2 | 40
1 | 20
(2 rows)
# SESSION 2 中查看到的數據
5 test=# select txid_current_if_assigned(); SESSION 2
txid_current_if_assigned
--------------------------
(1 row)
6 test=# select txid_current_if_assigned(); SESSION 2
txid_current_if_assigned
--------------------------
(1 row)
7 test=# delete from test where id =1 ; SESSION 1
DELETE 1
test=# select txid_current_if_assigned();
txid_current_if_assigned
--------------------------
2027737
(1 row)
#SESSION 1 刪除了數據
8 test=# update test set age = 100 where id =1 ; SESSION 2 WAITING...................
UPDATE 0
test=# select txid_current_if_assigned();
txid_current_if_assigned
--------------------------
2027738
(1 row)
#SESSION 2 更新數據 處於等待狀態
9 test=# select xmin,xmax,cmin,cmax,* from test; SESSION 1
xmin | xmax | cmin | cmax | id | age
---------+------+------+------+----+-----
2027732 | 0 | 0 | 0 | 2 | 40
(1 row)
10test=# select txid_current_if_assigned(); SESSION 1
txid_current_if_assigned
--------------------------
2027737
(1 row)
11 test=# select xmin,xmax,cmin,cmax,* from test; SESSION 2
xmin | xmax | cmin | cmax | id | age
---------+------+------+------+----+-----
2027732 | 0 | 0 | 0 | 2 | 40
2027737 | 0 | 1 | 1 | 1 | 20
(2 rows)
#SESSION 1 插入數據
12 test=# insert into test (id,age) values (1,20); SESSION 1
INSERT 0 1
test=# select txid_current_if_assigned();
txid_current_if_assigned
--------------------------
2027737
(1 row)
13 test=# select xmin,xmax,cmin,cmax,* from test; SESSION 1
xmin | xmax | cmin | cmax | id | age
---------+------+------+------+----+-----
2027732 | 0 | 0 | 0 | 2 | 40
2027737 | 0 | 1 | 1 | 1 | 20
(2 rows)
14 test=# commit; SESSION 1
COMMIT
#SESSION 1 COMMIT
15 test=# select xmin,xmax,cmin,cmax,* from test; SESSION 1
xmin | xmax | cmin | cmax | id | age
---------+------+------+------+----+-----
2027732 | 0 | 0 | 0 | 2 | 40
2027737 | 0 | 1 | 1 | 1 | 20
(2 rows)
16 test=# commit; SESSION 2
COMMIT
#SESSION 2 COMMIT
17 test=# select xmin,xmax,cmin,cmax,* from test; SESSION 2
xmin | xmax | cmin | cmax | id | age
---------+------+------+------+----+-----
2027732 | 0 | 0 | 0 | 2 | 40
2027737 | 0 | 1 | 1 | 1 | 20
(2 rows)
結果:SESSION 2 不會更新 SESSION 1中後插入的數據。
從上面的步驟中我們能看到或者領會到PG 的那些特性
事務ID 是自增的
每行數據會用(t_xmin, t_xmax)來標示自己的可用性
t_xmin 存儲的是產生這個元組的事務ID,可能是insert或者update語句t_xmax 存儲的是刪除或者鎖定這個元組的XID
事務只能看見t_xmin比自己XID 小且沒有被刪除的元組
以上是官方文檔中的提示,已經明確的說明了上述的問題,並且也給出了一些建議。
那我們的工作到底完成了沒有,沒有,我們提升PG 的隔離級別到 RR.
結果如下:
SESSION 1
SESSION 2
在將SESSION的級別提升後,結果就變化了,再次在SESSION 2中操作,直接報錯。
最後的問題是,提出問題的同學反映ORACLE 與PG的在類似的環節情況下,反饋的情況不一。同時我這邊也通過MYSQL 8 來將上述的操作同樣做了,與那位同學反映的情況一樣。
個人認爲這並不是PG數據庫本身的缺陷,這是一種數據庫處理某種複雜情況和隔離級別對數據一致性的一種取捨。
如果遇到這樣的情況如何操作,有如下建議
1 可以提高數據庫的隔離級別到RR (如果你的數據庫中有類似業務或操作)
2 在設計業務邏輯時,通過邏輯刪除而不是物理刪除來對業務表進行操作。
3 可以在操作時添加 selecr for update 類似這樣的語句對於數據的可操作性進行一個確認。