參考書籍: 《PostgreSQL10 High Performance》
MVCC即Multi Version Concurrency Control多版本併發控制,爲提升多用戶訪問同樣的數據而設計,性能優於鎖定。被廣泛用於諸多主流數據庫。
內部可見性計算
當一個事務創建時,數據庫會更新一個事務ID計算器,通常叫做XID。
- 當插入一行或更新數據時,一個新行被創建(更新也會創建新行),該新行有一個字段insertion XID(通常稱作xmin),記錄了當前事務ID
- 當刪除數據時,每個被刪除行有個delete XID(通常稱作xmax),當行未被刪除時值爲空
select txid_current(),xmin,xmax,id from test
txid_current()函數返回當前查詢的XID
如果一個查詢開始,行數據被找到的條件爲
(查詢XID >= xmin) && ((查詢XID <= xmax) || 查詢XID is null)
每個數據庫客戶會話都允許同時修改表,但他們只有在事務提交後纔對其他會話可見。
示例
會話1啓動事務並更新數據但不提交
# begin;
BEGIN
# select txid_current(),xmin,xmax,* from test;
txid_current | xmin | xmax | id | name
--------------+----------+------+----+------
81911792 | 81911784 | 0 | 1 | 1
81911792 | 81911785 | 0 | 2 | 2
(2 rows)
# update test set name='new' where id=2;
UPDATE 1
dd=# select txid_current(),xmin,xmax,* from test;
txid_current | xmin | xmax | id | name
--------------+----------+------+----+------
81911792 | 81911784 | 0 | 1 | 1
81911792 | 81911792 | 0 | 2 | new
(2 rows)
會話2 查詢數據
因會話1未提交,會話2XID大於修改數據的XMAX值,因此看不到最新版本數據,只能看到老版本未修改的數據
# select txid_current(),xmin,xmax,* from test;
txid_current | xmin | xmax | id | name
--------------+----------+----------+----+------
81911793 | 81911784 | 0 | 1 | 1
81911793 | 81911785 | 81911792 | 2 | 2
(2 rows)
會話1 提交
提交後xmax被清空
# commit;
COMMIT
如果會話沒有提交而是回滾,則直接刪除新版本數據即可。
會話2再次查詢(事務中)或新會話 查詢數據
因爲xmax被清空,因此可以查到提交的數據
# select txid_current(),xmin,xmax,* from test;
txid_current | xmin | xmax | id | name
--------------+----------+------+----+------
81911794 | 81911784 | 0 | 1 | 1
81911794 | 81911792 | 0 | 2 | new
(2 rows)
dd=# commit;
COMMIT
更新數據行的實際過程
- 讀入原行數據
- 根據update修改數據字段
- 將新數據行及其XID寫入到新的磁盤
- 舊數據行在不再被使用後,可以被Vacuum刪除
刪除數據行的實際過程
- 標記原行數據爲刪除,並更新XID
- 舊數據行在不再被使用後,可以被Vacuum刪除
Heap only tuples(HOT)
上文提到更新和刪除的過程,HOT在特定條件下允許直接重用更新和刪除操作數據行後的磁盤空間。
例如更新行沒有更新任何索引字段,如果新數據行可以被當前數據行所在數據頁剩餘空間容納下時。將觸發一個數據塊上的mini vacuum。正常的Vacuum需要更新數據heap和Index,而mini vacuum只更新數據heap,因此稱爲heap only。
pg_stat_user_tables視圖記錄了表上所有更新次數n_tup_upd和HOT更新次數n_tup_hot_upd
MVCC優缺點
優點
最大程度避免了鎖定,讀數據不會與寫數據發生衝突,讀不會阻塞寫,寫也不會阻塞讀。
缺點
- 佔用了磁盤空間,需要進行Vacuum清掃
- 更新時使用了新的磁盤,老數據未被刪除
- 刪除時老數據未被真正刪除磁盤
- 要當心不可重複讀和髒讀問題
事務ID環繞
事務ID是32比特,大約20億個ID,超出則會變爲0。MVVC的可見機制將會出現問題。
爲了解決問題
- 每個數據庫和每張表都有一個引用XID,可見於pg_class表relfrozenxid字段,該字段記錄本表或數據庫最小XID,小於該值得XID都見被置爲保留的凍結ID,Vacuum將處理這些XID