PostgreSQL中的多版本併發控制-MVCC

1 PostgreSQL中的多版本併發控制-MVCC 1

1.1 爲什麼需要MVCC 1

1.2 不同的MVCC機制 1

1.3 MVCC 設計的幾個概念 1

1.4 MVCC的工作機制 2

1.1.1 插入數據實例 2

1.1.2 修改數據實例 3

1.1.3 刪除數據實例 4

1.1.4 數據操作總結來說 5

1 PostgreSQL中的多版本併發控制-MVCC

MVCC , Multi - Version Concurrency Control , 多版本控制併發

1.1 爲什麼需要MVCC

數據庫在併發操作下,如果數據正在寫,而用戶又在讀,可能會出現數據不一致的問題,比如一行數據只寫入了前半部分,後半部分還沒有寫入,而此時用戶讀取這行數據時就會出現前半部分是新數據,後半部分是舊數據的現象,造成前後數據不一致問題,解決這個問題最好的方法就是讀寫加鎖,寫的時候不允許讀,讀的時候不允許寫,不過這樣就降低了數據庫的併發性能,因此便引入了MVCC的概念,它的目的便是實現讀寫事務相互不阻塞,從而提高數據庫的併發性能。

1.2 不同的MVCC機制

實現MVCC的機制有兩種:

1、寫入數據時,把舊版本數據移到其他地方,如回滾等操作,在回滾中把數據讀出來。

2、寫入數據庫時,保留舊版本的數據,並插入新數據

像oracle數據庫使用的是第一種方式,postgresql使用的是第二種方式。

1.3 MVCC 設計的幾個概念

1、事務ID
 在postgresql中,每個事務都存在一個唯一的ID,也稱爲xid,可通過txid_current()函數獲取當前的事務ID
2、tupe
每一行數據,稱爲一行元祖,一個tupe
3、ctid
tuple中的隱藏字段,代表tuple的物理位置
4、xmin
tuple 中的隱藏字段,在創建一個tuple時,記錄此值爲當前的事務ID
5、xmax
tuple 中的隱藏字段,默認爲0,在刪除時,記錄此值爲當前的事務的ID
6、cmin/cmax
tuple中的隱藏字段,表示同一個事務中多個語句的順序,從0開始

1.4 MVCC的工作機制

Postgresql中的MVCC就是通過以上幾個隱藏字段協作同實現的,下面舉幾個例子來看下工作機制

1.1.1 插入數據實例

1、首先我們開啓事務插入一條數據,其中ctid代表數據的物理位置,xmin爲當前事務ID,xmax爲0

postgres=# create table test(id int,name varchar(50));
CREATE TABLE
postgres=# begin transaction;
BEGIN
postgres=# select txid_current();
 txid_current 
--------------
 535
(1 row)
postgres=# insert into test(id,name) values(1,'a');
INSERT 0 1
postgres=# insert into test(id,name) values(2,'b');
INSERT 0 1
postgres=# select ctid,xmin,xmax,cmin,cmax,* from test;
 ctid | xmin | xmax | cmin | cmax | id | name 
-------+------+------+------+------+----+------
 (0,1) | 535 | 0 | 0 | 0 | 1 | a
 (0,2) | 535 | 0 | 1 | 1 | 2 | b
(2 rows)
postgres=# commit;
COMMIT
postgres=# select ctid,xmin,xmax,cmin,cmax,* from test;
 ctid | xmin | xmax | cmin | cmax | id | name 
-------+------+------+------+------+----+------
 (0,1) | 535 | 0 | 0 | 0 | 1 | a
 (0,2) | 535 | 0 | 1 | 1 | 2 | b
(2 rows)
繼續在上一個事務中再插入一條數據,因爲在同一個事務中,可以看到cmin,cmax按順序增長

1.1.2 修改數據實例

修改ID爲1的數據name爲d,此時ID爲1的ctid變爲了(0,4),同時開啓另外一個窗口,可以看到ID爲1的xmax標識爲修改數據時的事務ID,既代表詞條tuple已刪除。

-- 第一個窗口
postgres=# insert into test(id,name) values(3,'c');
postgres=# begin transaction;
BEGIN
postgres=# select txid_current();
 txid_current 
--------------
 537
(1 row)
postgres=# update test set name = 'd' where id ='1';
UPDATE 1
postgres=# select ctid,xmin,xmax,cmin,cmax,* from test;
 ctid | xmin | xmax | cmin | cmax | id | name 
-------+------+------+------+------+----+------
 (0,2) | 535 | 0 | 1 | 1 | 2 | b
 (0,3) | 536 | 0 | 0 | 0 | 3 | c
 (0,4) | 537 | 0 | 0 | 0 | 1 | d
(3 rows)
-- 第二個窗口
postgres=# begin transaction;
BEGIN
postgres=# select txid_current();
 txid_current 
--------------
 538
(1 row)
postgres=# select ctid,xmin,xmax,cmin,cmax,* from test;
 ctid | xmin | xmax | cmin | cmax | id | name 
-------+------+------+------+------+----+------
 (0,1) | 535 | 537 | 0 | 0 | 1 | a
 (0,2) | 535 | 0 | 1 | 1 | 2 | b
 (0,3) | 536 | 0 | 0 | 0 | 3 | c
(3 rows)
第一個窗口connit後在第二個窗口查詢顯示
postgres=# select ctid,xmin,xmax,cmin,cmax,* from test;
 ctid | xmin | xmax | cmin | cmax | id | name 
-------+------+------+------+------+----+------
 (0,2) | 535 | 0 | 1 | 1 | 2 | b
 (0,3) | 536 | 0 | 0 | 0 | 3 | c
 (0,4) | 537 | 0 | 0 | 0 | 1 | d
(3 rows)

1.1.3 刪除數據實例

刪除ID爲1的數據,另開啓一個窗口,可以看到ID爲1的xmax爲刪除操作的事務ID,代表此條tuple刪除。

-- 第一個窗口操作如下
postgres=# begin transaction;
BEGIN
postgres=# select txid_current();
 txid_current 
--------------
 539
(1 row)
postgres=# delete from test where id = 1;
DELETE 1
postgres=# select ctid,xmin,xmax,cmin,cmax,* from test;
 ctid | xmin | xmax | cmin | cmax | id | name 
-------+------+------+------+------+----+------
 (0,2) | 535 | 0 | 1 | 1 | 2 | b
 (0,3) | 536 | 0 | 0 | 0 | 3 | c
(2 rows)
-- 第二個窗口操作如下
postgres=# begin transaction;
BEGIN
postgres=# select txid_current();
 txid_current 
--------------
 541
(1 row)
postgres=# select ctid,xmin,xmax,cmin,cmax,* from test;
 ctid | xmin | xmax | cmin | cmax | id | name 
-------+------+------+------+------+----+------
 (0,2) | 535 | 0 | 1 | 1 | 2 | b
 (0,3) | 536 | 0 | 0 | 0 | 3 | c
 (0,4) | 537 | 539 | 0 | 0 | 1 | d
(3 rows)
-- 第一個窗口提交事務,第二個不提交事務,查看第二個窗口的數據信息
-- 第一個窗口操作
postgres=# commit;
COMMIT
-- 查看第二個窗口信息
postgres=# select ctid,xmin,xmax,cmin,cmax,* from test;
 ctid | xmin | xmax | cmin | cmax | id | name 
-------+------+------+------+------+----+------
 (0,2) | 535 | 0 | 1 | 1 | 2 | b
 (0,3) | 536 | 0 | 0 | 0 | 3 | c
(2 rows)

1.1.4 數據操作總結來說

1、數據文件中同一邏輯行存在多個版本

2、每個版本通過隱藏字段記錄着它的創建事務的ID,刪除事務ID等信息

3、通過一定的邏輯保證每個事務能夠看到一個特定的版本

讀寫事務工作在不同的版本上,以保證讀寫不衝突。


更多文章請關注作者公衆號

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章