論Postgres的“已提交的而且xmin’比當前事務的XID小的記錄對當前事務才是可見的”
1.闡述
最近在網上看到這樣一句話Postgres“已提交的而且 xmin 比當前事務的XID小的記錄對當前事務才是可見的”。先不評斷這句話的正確性;看下這句話的結構,因果關係;
按照此話的意思;要postgres中的資料可見必須滿足兩個必要條件:
- 事務已經提交(commit);
- 提交時插入記錄的xmin 小於 當前current_txid(事務id)。
而網上對這句話的解釋:“這意味著,你可以開始一個新事務然後插入一行記錄,直到你提交(COMMIT)之前,你插入的這行記錄對其他事務永遠都是不可見的。等到提交以後,其他後建立的新事務就可以看到這行新記錄了,因為他們滿足了 xmin < XID 條件,而且建立哪一行記錄的事務也已經完成”。看起來挺合理的,無懈可擊似的。接下來我們來推敲推敲。要說記錄的可見性;這還得從事務的隔離級別說起。
2. 舉例驗證
根據《PostgreSQL9.4.4-CN-v1.0.pdf》文件介紹: SQL標準定義了四個級別的事務隔離 { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }; postgres目前只實現了 {SERIALIZABLE | REPEATABLE READ | READ COMMITTED }這三種。詳細大家去看文件;這裡不做介紹。
- 事務隔離級別READ COMMITTED
事務隔離級別:讀已提交(READ COMMITTED)這是postgres,greenplum預設的事務隔離級別。若從先事務隔離級別(讀已提交)來解釋:就是讀已經提交的記錄;是不是這樣呢? 來驗證下。
--session A 事務id為1844;
postgres=# begin;
BEGIN
postgres=# select txid_current();
txid_current
--------------
1844
--session B 事務id為1845;並在插入一條記錄在lottu05表(未提交)
postgres=# begin;
BEGIN
postgres=# select txid_current();
txid_current
--------------
1845
postgres=# insert into lottu05 values (1001,`lottu`);
INSERT 0 1
--在session A/B檢視記錄; session A讀不到記錄; session B可以讀到記錄。
postgres=# select * from lottu05;
id | name
----+------
(0 rows)
--在session B提交插入的記錄;在檢視session A是否可以看到記錄。
postgres=# select xmin,* from lottu05;
xmin | id | name
------+------+-------
1845 | 1001 | lottu
--表明session A(當前事務為ID:1844)可以讀 插入記錄事務id為1845 已經提交的記錄。
--總結: 事務隔離級別為讀已提交(READ COMMITTED)就是讀已經提交的記錄。
由此可見,對讀已提交隔離級別而言"已提交的而且 xmin’比當前事務的XID小的記錄對當前事務才是可見的"是不正確的。
而網上的解釋:也是必要不充分條件。那該如何詮釋這說話呢?請看下文講解
是根據當前postgres系統的當前事務ID相比;目前系統下一個事務ID為1846
-- 我們現在看下當前postgres系統 下一個事務id
[postgres@localhost ~]$ pg_controldata |grep NextXID
Latest checkpoint`s NextXID: 0/1846
--意思是說這條記錄後面開啟會話從事務id:1846是可見的。不充分的是事務ID:1844也可以讀到該記錄。
--然而這句話來源何處;我想是有依據的。接下來我們做一個實驗。模擬postgrs穿越到過去。
--session C 現在插入1002-1008條記錄;結果如下:
postgres=# select xmin,id,name from lottu05;
xmin | id | name
------+------+---------
1845 | 1001 | lottu
1846 | 1002 | lottu02
1847 | 1003 | lottu03
1848 | 1004 | lottu04
1849 | 1005 | lottu05
1850 | 1006 | lottu06
1851 | 1007 | lottu07
1852 | 1008 | lottu08
--我們現在使用將資料庫postgres回到 txid 為1849。注意:該動作不建議操作;
[postgres@localhost ~]$ pg_stop
waiting for server to shut down.......... done
server stopped
[postgres@localhost ~]$ pg_resetxlog -x 1849 $PGDATA
Transaction log reset
[postgres@localhost ~]$ pg_start
server starting
[postgres@localhost ~]$ psql
psql (9.5.0)
Type "help" for help.
postgres=# select xmin,id,name from lottu05;
xmin | id | name
------+------+---------
1845 | 1001 | lottu
1846 | 1002 | lottu02
1847 | 1003 | lottu03
1848 | 1004 | lottu04
1849 | 1005 | lottu05
--可以看到上面的xmin:(1850-1852)是不可見的。
--等資料庫的事務ID超過1852;這些資料可以展示出來。
postgres=# select txid_current();
txid_current
--------------
1850
postgres=# select txid_current();
txid_current
--------------
1851
postgres=# select txid_current();
txid_current
--------------
1852
postgres=# select xmin,id,name from lottu05;
xmin | id | name
------+------+---------
1845 | 1001 | lottu
1846 | 1002 | lottu02
1847 | 1003 | lottu03
1848 | 1004 | lottu04
1849 | 1005 | lottu05
1850 | 1006 | lottu06
1851 | 1007 | lottu07
1852 | 1008 | lottu08
--從這個實驗看來 確實是需要滿足網上所說的兩個條件。上面也提過;該操作不建議操作。設想;當前時代若可以穿越到歷史上各個時代;那歷史不亂套了嗎?同理如此。
所以說對隔離級別為READ COMMITTED而言;如同它字面解釋一樣;只要記錄COMMITTED;就可以讀到。
注意:
--1.該操作不等同 oracle的flashback操作;雖然回到了歷史;歷史上已經發生的還是會發生。
--2.該操作並不能做資料恢復操作。若對資料做刪除進行恢復;可以參考--http://www.cnblogs.com/lottu/p/5761885.html
總結:對隔離級別為READ COMMITTED而言;如同它字面解釋一樣;只要記錄COMMITTED;就可以讀到
- 事務隔離級別:REPEATABLE READ
事務隔離級別:REPEATABLE READ;是不是如同它而言呢?接下來拭目以待吧。
3.總結
--開啟SESSION A; ctid為1857。
postgres=# truncate table lottu05;
TRUNCATE TABLE
postgres=# begin;
BEGIN
postgres=# select txid_current();
txid_current
--------------
1857
--開啟session B;隔離級別為REPEATABLE READ。事務id為:1858
postgres=# begin ISOLATION LEVEL REPEATABLE READ;
BEGIN
postgres=# select txid_current();
txid_current
--------------
1858
--在session A插入 10條記錄並提交
postgres=# insert into lottu05 select generate_series(1001,1010),`lottu`||generate_series(1,10);
INSERT 0 10
postgres=# commit;
COMMIT
--在session B檢視是否可以讀到記錄
postgres=# select * from lottu05;
id | name
----+------
(0 rows)
--結果表明session B 讀不到 已經提交且 事務ID:1857比session B的事務ID為1858要小的記錄。
3.總結
對Postgres記錄的可見性;對網上這句話“已提交的而且 xmin 比當前事務的XID小的記錄對當前事務才是可見的”可以修正為“已提交的而且 xmin 比當前系統事務的XID小或者等於的記錄對當前事務才是可見的”。而對它的解釋(這意味著,你可以開始一個新事務然後插入一行記錄,直到你提交(COMMIT)之前,你插入的這行記錄對其他事務永遠都是不可見的。等到提交以後,其他後建立的新事務就可以看到這行新記錄了,因為他們滿足了 xmin < XID 條件,而且建立哪一行記錄的事務也已經完成”)是充分不必要;
–參考文獻
相關文章
- 解決SELinux對網站目錄許可權控制的不當的問題Linux網站
- 當初自學C++時的筆記記錄C++筆記
- 當年,我是如何把微服務落地的微服務
- 常見的集合容器應當避免的坑
- 當下的前端框架和未來前端框架對比前端框架
- 獲取當前修改的行記錄資料
- 查詢當天的系統訂單記錄
- 有容雲-PPT | 當微服務遇見容器微服務
- flowable 獲取當前任務流程圖片的輸入流流程圖
- activiti 獲取當前任務流程圖片的輸入流流程圖
- 探究MySQL的DML提交事務的意義和DQL是否有必要提交事務MySql
- 理解PG的xmin和xmax的幾個小實驗
- 刪除當前目錄下的所有可執行檔案
- 都討論大廠面試,當我小廠面試請喝茶的?面試
- [置頂] 如何處理:BOF或EOF中有一個是“真”,或者當前的記錄已被刪除,所需的操作要求一個當前的記錄...
- Activiti 流程圖上標記當前任務流程圖
- Jquery對當前日期的操作(格式化當前日期)jQuery
- golang當中對select的理解Golang
- git獲取當前所在的目錄Git
- MySQL 當記錄不存在時插入,當記錄存在時更新MySql
- 未提交事務造成的等待事件事件
- 檢視mysql沒提交的事務MySql
- Spring中的事務提交事件Spring事件
- 當代大學生對學習Linux的一點拙見Linux
- 又見想當然導致的誤譯
- Android可見APP的不可見任務棧(TaskRecord)銷燬分析AndroidAPP
- 【LINUX學習】檢視當前所有服務的狀態Linux
- 當API成為服務API
- tbase和postgres-xl的比較
- 事務標識(xid)解析
- 對事務的理解
- 小程式 分享朋友圈當中的坑
- Linux - 常見埠和服務的對照和解釋Linux
- Git的修改提交記錄和變基Git
- 當前比較好用的golang的redis客戶端有哪些?GolangRedis客戶端
- 以樹狀結構顯示系統當前的任務(轉)
- 當我們談微服務,我們在談什麼 (3) — 如何保障微服務的穩定性微服務
- hystrix對比服務網格istio的destinationrule