Postgresql 探索MVCC

Postgresql MVCC

Postgresql的隱藏列

  1. tableoid
    是表對象的一個惟一標識符,能夠和pg_class中的oid聯合起來查看sql

  2. xmin
    是插入的事務標識符,是用來標識不一樣事務下的一個版本控制數據庫

  3. xmax
    是刪除更新的事務標識符,若是該值不爲0,則說明該行數據當前還未提交或回滾異步

  4. cmin
    插入事務的命令標識符,從0開始post

  5. cmax
    刪除事務的命令標識符,或者爲0版本控制

  6. ctid
    是每行數據在表中的一個物理位置標識符postgresql

下面舉例說明:code

t1=# create table test (id integer, value text);
CREATE TABLE
t1=# insert into test values (1, 'a'), (2, 'aa'), (3, 'aaa');
INSERT 0 3
t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
 cmin | cmax |   xmin   | xmax | ctid  | id | value 
------+------+----------+------+-------+----+-------
    0 |    0 | 75066031 |    0 | (0,1) |  1 | a
    0 |    0 | 75066031 |    0 | (0,2) |  2 | aa
    0 |    0 | 75066031 |    0 | (0,3) |  3 | aaa
(3 rows)

 xmin: 75066031 是插入數據的事務id
 xmax: 0 表示已經提交了
 ctid: (0, 1), (0, 2), (0, 3)是tuple 所在table中的位置
 
 t1=# begin;
 BEGIN
 t1=# select tableoid from test;
    tableoid 
 ----------
    96972
    96972
    96972
 (3 rows)

t1=# insert into test values (4, 'b');
INSERT 0 1
t1=# insert into test values (5, 'bb');
INSERT 0 1
t1=# insert into test values (6, 'bbb');
INSERT 0 1
t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
cmin | cmax |   xmin   | xmax | ctid  | id | value 
------+------+----------+------+-------+----+-------
0 |    0 | 75066031 |    0 | (0,1) |  1 | a
0 |    0 | 75066031 |    0 | (0,2) |  2 | aa
0 |    0 | 75066031 |    0 | (0,3) |  3 | aaa
0 |    0 | 75066040 |    0 | (0,4) |  4 | b
1 |    1 | 75066040 |    0 | (0,5) |  5 | bb
2 |    2 | 75066040 |    0 | (0,6) |  6 | bbb
(6 rows)

t1=# commit;
COMMIT

tableoid: 是表的oid
探索postgresql MVCC 原理
首先打開兩個psql
t1=# begin;
BEGIN
t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
cmin | cmax |   xmin   | xmax | ctid  | id | value 
------+------+----------+------+-------+----+-------
0 |    0 | 75066031 |    0 | (0,1) |  1 | a
0 |    0 | 75066031 |    0 | (0,2) |  2 | aa
0 |    0 | 75066031 |    0 | (0,3) |  3 | aaa
0 |    0 | 75066040 |    0 | (0,4) |  4 | b
1 |    1 | 75066040 |    0 | (0,5) |  5 | bb
2 |    2 | 75066040 |    0 | (0,6) |  6 | bbb
(6 rows)

t1=# update test set value = 'c' where id = 4;
UPDATE 1
t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
cmin | cmax |   xmin   | xmax | ctid  | id | value 
------+------+----------+------+-------+----+-------
0 |    0 | 75066031 |    0 | (0,1) |  1 | a
0 |    0 | 75066031 |    0 | (0,2) |  2 | aa
0 |    0 | 75066031 |    0 | (0,3) |  3 | aaa
1 |    1 | 75066040 |    0 | (0,5) |  5 | bb
2 |    2 | 75066040 |    0 | (0,6) |  6 | bbb
0 |    0 | 75066045 |    0 | (0,7) |  4 | c
(6 rows)
t1=# select txid_current();
txid_current 
--------------
    75066045
(1 row)

從上面的數據能夠看出當數據庫作一個更新操做時,並非將老的數據刪除,再將新的數據覆蓋上去,相反它會把老的數據作一個標記隔離出去,而後再新增新的數據做爲一個新的版本

如今看另外一個psql
t1=# begin;
BEGIN
t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
 cmin | cmax |   xmin   |   xmax   | ctid  | id | value 
------+------+----------+----------+-------+----+-------
0 |    0 | 75066031 |        0 | (0,1) |  1 | a
0 |    0 | 75066031 |        0 | (0,2) |  2 | aa
0 |    0 | 75066031 |        0 | (0,3) |  3 | aaa
0 |    0 | 75066040 | 75066045 | (0,4) |  4 | b
1 |    1 | 75066040 |        0 | (0,5) |  5 | bb
2 |    2 | 75066040 |        0 | (0,6) |  6 | bbb
(6 rows)


從上面的數據逆推當Update時postgres 至少作三個動做
1. copy olddata to newplace
2. update data
3. add new transaction id to old data xmax

postgresq Delete 會更簡單一些只須要在tuple 上作一個標記.

既然postgres不會直接在olddata上修改,又是如何對這些tuple作隔離的呢? 簡而言之postgres 如何判斷這些tuple 是否對一個事務可見。
分析:
1. 對於tuple 的 xmin > 自身事務id 的row 必定是不可見
2. 對於tuple xmin < 自身事務id 的row而且已經提交的事務可見(deleted tuple 除外)

對於第一條規則很好判斷(在自身事務以後的動做必定是看不見的)。 第二條規則困難一些須要判斷一個事務是否提交,(可能還需判斷tuple是不是deleted。由於postgresql vacuum 是異步刪除deleted tuple)
對於這個問題postgres在tuple的header 里加入一個屬性 t_infomask 用於標記transaction的狀態.
Extension Pageinspect
select * from pg_available_extensions;
create extension pageinspect;
t1=# begin;
BEGIN
t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
 cmin | cmax |   xmin   | xmax | ctid  | id | value 
------+------+----------+------+-------+----+-------
    0 |    0 | 75066031 |    0 | (0,1) |  1 | a
    0 |    0 | 75066031 |    0 | (0,2) |  2 | aa
    0 |    0 | 75066031 |    0 | (0,3) |  3 | aaa
    1 |    1 | 75066040 |    0 | (0,4) |  5 | bb
    2 |    2 | 75066040 |    0 | (0,5) |  6 | bbb
    0 |    0 | 75066110 |    0 | (0,6) |  4 | b
(6 rows)

t1=# SELECT * FROM heap_page_items(get_raw_page('test', 0));
 lp | lp_off | lp_flags | lp_len |  t_xmin  | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid 
----+--------+----------+--------+----------+--------+----------+--------+-------------+------------+--------+--------+-------
  1 |   8160 |        1 |     30 | 75066031 |      0 |        0 | (0,1)  |           2 |       2818 |     24 |        |      
  2 |   8128 |        1 |     31 | 75066031 |      0 |        0 | (0,2)  |           2 |       2818 |     24 |        |      
  3 |   8096 |        1 |     32 | 75066031 |      0 |        0 | (0,3)  |           2 |       2818 |     24 |        |      
  4 |   8064 |        1 |     31 | 75066040 |      0 |        1 | (0,4)  |           2 |       2818 |     24 |        |      
  5 |   8032 |        1 |     32 | 75066040 |      0 |        2 | (0,5)  |           2 |       2818 |     24 |        |      
  6 |   8000 |        1 |     30 | 75066110 |      0 |        0 | (0,6)  |           2 |      11010 |     24 |        |      
(6 rows)

t1=# update test set value = 'c' where id = 4;
UPDATE 1
t1=# SELECT * FROM heap_page_items(get_raw_page('test', 0));
 lp | lp_off | lp_flags | lp_len |  t_xmin  |  t_xmax  | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid 
----+--------+----------+--------+----------+----------+----------+--------+-------------+------------+--------+--------+-------
  1 |   8160 |        1 |     30 | 75066031 |        0 |        0 | (0,1)  |           2 |       2818 |     24 |        |      
  2 |   8128 |        1 |     31 | 75066031 |        0 |        0 | (0,2)  |           2 |       2818 |     24 |        |      
  3 |   8096 |        1 |     32 | 75066031 |        0 |        0 | (0,3)  |           2 |       2818 |     24 |        |      
  4 |   8064 |        1 |     31 | 75066040 |        0 |        1 | (0,4)  |           2 |       2818 |     24 |        |      
  5 |   8032 |        1 |     32 | 75066040 |        0 |        2 | (0,5)  |           2 |       2818 |     24 |        |      
  6 |   8000 |        1 |     30 | 75066110 | 75066121 |        0 | (0,7)  |       16386 |       8962 |     24 |        |      
  7 |   7968 |        1 |     30 | 75066121 |        0 |        0 | (0,7)  |       32770 |      10242 |     24 |        |      
(7 rows)

t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
 cmin | cmax |   xmin   | xmax | ctid  | id | value 
------+------+----------+------+-------+----+-------
    0 |    0 | 75066031 |    0 | (0,1) |  1 | a
    0 |    0 | 75066031 |    0 | (0,2) |  2 | aa
    0 |    0 | 75066031 |    0 | (0,3) |  3 | aaa
    1 |    1 | 75066040 |    0 | (0,4) |  5 | bb
    2 |    2 | 75066040 |    0 | (0,5) |  6 | bbb
    0 |    0 | 75066121 |    0 | (0,7) |  4 | c
(6 rows)

t1=# select txid_current();
 txid_current 
--------------
 75066121
(1 row)

Vacuum internals 文章實在太好了
Postgres Hint Bits對象

相關文章
相關標籤/搜索