PostgreSQL是經過MVCC(Multi-Version Concurrency Control)來保證事務的原子性和隔離性,具體MVCC機制是怎樣實現的,下面舉些示例來作個簡單解析以加深理解。併發
PostgreSQL的每一個表中都有些系統隱藏字段,包括:性能
MVCC機制經過這些隱藏的標記字段來協同實現,下面舉幾個示例來解釋MVCC是如何實現的spa
//seesion1: 建立表,顯示指定oid字段: testdb=# create table t1(id int) with oids; CREATE TABLE 插入幾條記錄 testdb=# insert into t1 values(1); INSERT 17569 1 testdb=# insert into t1 values(2); INSERT 17570 1 testdb=# insert into t1 values(3); INSERT 17571 1
查詢當前表中的tuple信息,xmin爲建立tuple時的事務ID,xmax默認爲0code
testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1; ctid | xmin | xmax | cmin | cmax | oid | id -------+----------+------+------+------+-------+---- (0,1) | 80853357 | 0 | 0 | 0 | 17569 | 1 (0,2) | 80853358 | 0 | 0 | 0 | 17570 | 2 (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3 (3 rows)
接下來,咱們更新某個tuple的字段,將tuple中id值爲1更新爲4,看看會發生什麼對象
testdb=# begin; BEGIN testdb=# select txid_current(); txid_current -------------- 80853360 (1 row) testdb=# update t1 set id = 4 where id = 1; UPDATE 1
查看tuple詳細信息索引
testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1; ctid | xmin | xmax | cmin | cmax | oid | id -------+----------+------+------+------+-------+---- (0,2) | 80853358 | 0 | 0 | 0 | 17570 | 2 (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3 (0,4) | 80853360 | 0 | 0 | 0 | 17569 | 4 (3 rows)
能夠看到id爲1的tuple(oid=17569)已經被修改了,id值被更新爲4,另外ctid、xmin字段也被更新了,ctid值表明了該tuple的物理位置,xmin值是建立tuple時都已經寫入,這兩個字段都不該該被更改纔對,另起一個seesion來看下(當前事務還未提交)事務
//seesion2: testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1; ctid | xmin | xmax | cmin | cmax | oid | id -------+----------+----------+------+------+-------+---- (0,1) | 80853357 | 80853360 | 0 | 0 | 17569 | 1 (0,2) | 80853358 | 0 | 0 | 0 | 17570 | 2 (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3 (3 rows)
能夠看到id爲1的tuple(oid=17569)還存在,只是xmax值被標記爲當前事務Id。 原來更新某個tuple時,會新增一個tuple,填入更新後的字段值,將原來的tuple標記爲刪除(設置xmax爲當前事務Id)。同理,能夠看下刪除一個tuple的結果ci
//seesion1: testdb=# delete from t1 where id = 2; DELETE 1 //seesion2: testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1; ctid | xmin | xmax | cmin | cmax | oid | id -------+----------+----------+------+------+-------+---- (0,1) | 80853357 | 80853360 | 0 | 0 | 17569 | 1 (0,2) | 80853358 | 80853360 | 1 | 1 | 17570 | 2 (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3 (3 rows)
刪除某個tuple時也是將xmax標記爲當前事務Id,並不作實際的物理記錄清除操做。另外cmin和cmax值遞增爲1,代表了同一事務中操做的順序性。在該事務(seesion1)未提交前,其餘事務(seesion2)能夠看到以前的版本信息,不一樣的事務擁有各自的數據空間,其操做不會對對方產生干擾,保證了事務的隔離性。it
提交事務,查看最終結果以下:io
//seesion1: testdb=# commit; COMMIT testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1; ctid | xmin | xmax | cmin | cmax | oid | id -------+----------+------+------+------+-------+---- (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3 (0,4) | 80853360 | 0 | 0 | 0 | 17569 | 4 (2 rows)
可是,若是咱們不提交事務而是回滾,結果又是如何?
testdb=# begin ; BEGIN testdb=# update t1 set id = 5 where id = 4; UPDATE 1 testdb=# rollback; ROLLBACK testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1; ctid | xmin | xmax | cmin | cmax | oid | id -------+----------+----------+------+------+-------+---- (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3 (0,4) | 80853360 | 80853361 | 0 | 0 | 17569 | 4 (2 rows) xmax標記並未清除,繼續新增一條記錄: testdb=# insert into t1 values(5); INSERT 17572 1 testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1; ctid | xmin | xmax | cmin | cmax | oid | id -------+----------+----------+------+------+-------+---- (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3 (0,4) | 80853360 | 80853361 | 0 | 0 | 17569 | 4 (0,6) | 80853362 | 0 | 0 | 0 | 17572 | 5 (3 rows)
發現沒有清理掉新增的tuple,消除原有tuple上的xmax標記,這是爲什麼?處於效率的緣由,若是事務回滾時也進行清除標記,可能會致使磁盤IO,下降性能。那如何判斷該tuple的是否有效呢?答案是PostgreSQL會把事務狀態記錄到clog(commit log)位圖文件中,每讀到一行時,會到該文件中查詢事務狀態,事務的狀態經過如下四種來表示:
事務的原子性(Atomicity)要求在同一事務中的全部操做要麼都作,要麼都不作。根據PostgreSQL的MVCC規則,插入數據時,會將當前事務ID寫入到xmin中,刪除數據時,會將事務ID寫入xmax中,更新數據至關於先刪除原來的tuple再新增一個tuple,增刪改操做都保留了事務ID,根據事務ID提交或撤銷該事務中的全部操做,從而保證了事務的原子性。
事務的隔離性(Isolation)要求各個並行事務之間不能相互干擾,事務之間是隔離的。PostgreSQL可讀取的數據是xmin小於當前的事務ID且已經提交。對某個tuple進行更新或刪除時,其餘事務讀取的就是這個tuple以前的版本。
PostgreSQL也須要事務ID來肯定事務的前後順序,PostgreSQL中,事務被稱爲XID,獲取當前XID:
testdb=# select txid_current(); txid_current -------------- 80853335 (1 row)
事務ID由32bit數字表示,當事務ID用完時,就會出現新的事務ID會比老ID小,致使事務ID回捲問題(Transaction
ID Wraparound)。 PostgreSQL的事務ID規則:
根據MVCC機制,更新和刪除的記錄都不會被實際刪除,操做頻繁的表會積累大量的過時數據,佔用磁盤空間,當掃描查詢數據時,須要更多的IO,下降查詢效率。PostgreSQL的解決方法是提供vacuum命令操做來清理過時的數據。