资讯专栏INFORMATION COLUMN

Postgresql 探索MVCC

张利勇 / 1885人阅读

摘要:分析对于的自身事务的一定是不可见对于自身事务的并且已经提交的事务可见除外对于第一条规则很好判断在自身事务之后的动作一定是看不见的。第二条规则困难一些需要判断一个事务是否提交可能还需判断是否是。

Postgresql MVCC Postgresql的隐藏列

tableoid
是表对象的一个唯一标识符,可以和pg_class中的oid联合起来查看

xmin
是插入的事务标识符,是用来标识不同事务下的一个版本控制

xmax
是删除更新的事务标识符,如果该值不为0,则说明该行数据当前还未提交或回滚

cmin
插入事务的命令标识符,从0开始

cmax
删除事务的命令标识符,或者为0

ctid
是每行数据在表中的一个物理位置标识符

下面举例说明:

</>复制代码

  1. t1=# create table test (id integer, value text);
  2. CREATE TABLE
  3. t1=# insert into test values (1, "a"), (2, "aa"), (3, "aaa");
  4. INSERT 0 3
  5. t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
  6. cmin | cmax | xmin | xmax | ctid | id | value
  7. ------+------+----------+------+-------+----+-------
  8. 0 | 0 | 75066031 | 0 | (0,1) | 1 | a
  9. 0 | 0 | 75066031 | 0 | (0,2) | 2 | aa
  10. 0 | 0 | 75066031 | 0 | (0,3) | 3 | aaa
  11. (3 rows)
  12. xmin: 75066031 是插入数据的事务id
  13. xmax: 0 表示已经提交了
  14. ctid: (0, 1), (0, 2), (0, 3)是tuple 所在table中的位置
  15. t1=# begin;
  16. BEGIN
  17. t1=# select tableoid from test;
  18. tableoid
  19. ----------
  20. 96972
  21. 96972
  22. 96972
  23. (3 rows)
  24. t1=# insert into test values (4, "b");
  25. INSERT 0 1
  26. t1=# insert into test values (5, "bb");
  27. INSERT 0 1
  28. t1=# insert into test values (6, "bbb");
  29. INSERT 0 1
  30. t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
  31. cmin | cmax | xmin | xmax | ctid | id | value
  32. ------+------+----------+------+-------+----+-------
  33. 0 | 0 | 75066031 | 0 | (0,1) | 1 | a
  34. 0 | 0 | 75066031 | 0 | (0,2) | 2 | aa
  35. 0 | 0 | 75066031 | 0 | (0,3) | 3 | aaa
  36. 0 | 0 | 75066040 | 0 | (0,4) | 4 | b
  37. 1 | 1 | 75066040 | 0 | (0,5) | 5 | bb
  38. 2 | 2 | 75066040 | 0 | (0,6) | 6 | bbb
  39. (6 rows)
  40. t1=# commit;
  41. COMMIT
  42. tableoid: 是表的oid
探索postgresql MVCC 原理

</>复制代码

  1. 首先打开两个psql
  2. t1=# begin;
  3. BEGIN
  4. t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
  5. cmin | cmax | xmin | xmax | ctid | id | value
  6. ------+------+----------+------+-------+----+-------
  7. 0 | 0 | 75066031 | 0 | (0,1) | 1 | a
  8. 0 | 0 | 75066031 | 0 | (0,2) | 2 | aa
  9. 0 | 0 | 75066031 | 0 | (0,3) | 3 | aaa
  10. 0 | 0 | 75066040 | 0 | (0,4) | 4 | b
  11. 1 | 1 | 75066040 | 0 | (0,5) | 5 | bb
  12. 2 | 2 | 75066040 | 0 | (0,6) | 6 | bbb
  13. (6 rows)
  14. t1=# update test set value = "c" where id = 4;
  15. UPDATE 1
  16. t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
  17. cmin | cmax | xmin | xmax | ctid | id | value
  18. ------+------+----------+------+-------+----+-------
  19. 0 | 0 | 75066031 | 0 | (0,1) | 1 | a
  20. 0 | 0 | 75066031 | 0 | (0,2) | 2 | aa
  21. 0 | 0 | 75066031 | 0 | (0,3) | 3 | aaa
  22. 1 | 1 | 75066040 | 0 | (0,5) | 5 | bb
  23. 2 | 2 | 75066040 | 0 | (0,6) | 6 | bbb
  24. 0 | 0 | 75066045 | 0 | (0,7) | 4 | c
  25. (6 rows)
  26. t1=# select txid_current();
  27. txid_current
  28. --------------
  29. 75066045
  30. (1 row)
  31. 从上面的数据可以看出当数据库做一个更新操作时,并不是将老的数据删除,再将新的数据覆盖上去,相反它会把老的数据做一个标记隔离出去,然后再新增新的数据作为一个新的版本
  32. 现在看另一个psql
  33. t1=# begin;
  34. BEGIN
  35. t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
  36. cmin | cmax | xmin | xmax | ctid | id | value
  37. ------+------+----------+----------+-------+----+-------
  38. 0 | 0 | 75066031 | 0 | (0,1) | 1 | a
  39. 0 | 0 | 75066031 | 0 | (0,2) | 2 | aa
  40. 0 | 0 | 75066031 | 0 | (0,3) | 3 | aaa
  41. 0 | 0 | 75066040 | 75066045 | (0,4) | 4 | b
  42. 1 | 1 | 75066040 | 0 | (0,5) | 5 | bb
  43. 2 | 2 | 75066040 | 0 | (0,6) | 6 | bbb
  44. (6 rows)
  45. 从上面的数据逆推当Update时postgres 至少做三个动作
  46. 1. copy olddata to newplace
  47. 2. update data
  48. 3. add new transaction id to old data xmax
  49. postgresq Delete 会更简单一些只需要在tuple 上做一个标记.
  50. 既然postgres不会直接在olddata上修改,又是如何对这些tuple做隔离的呢? 简而言之postgres 如何判断这些tuple 是否对一个事务可见。
  51. 分析:
  52. 1. 对于tuple 的 xmin > 自身事务id 的row 一定是不可见
  53. 2. 对于tuple xmin < 自身事务id 的row并且已经提交的事务可见(deleted tuple 除外)
  54. 对于第一条规则很好判断(在自身事务之后的动作一定是看不见的)。 第二条规则困难一些需要判断一个事务是否提交,(可能还需判断tuple是否是deleted。因为postgresql vacuum 是异步删除deleted tuple)
  55. 对于这个问题postgres在tuple的header 里加入一个属性 t_infomask 用于标记transaction的状态.
Extension Pageinspect

</>复制代码

  1. select * from pg_available_extensions;
  2. create extension pageinspect;
  3. t1=# begin;
  4. BEGIN
  5. t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
  6. cmin | cmax | xmin | xmax | ctid | id | value
  7. ------+------+----------+------+-------+----+-------
  8. 0 | 0 | 75066031 | 0 | (0,1) | 1 | a
  9. 0 | 0 | 75066031 | 0 | (0,2) | 2 | aa
  10. 0 | 0 | 75066031 | 0 | (0,3) | 3 | aaa
  11. 1 | 1 | 75066040 | 0 | (0,4) | 5 | bb
  12. 2 | 2 | 75066040 | 0 | (0,5) | 6 | bbb
  13. 0 | 0 | 75066110 | 0 | (0,6) | 4 | b
  14. (6 rows)
  15. t1=# SELECT * FROM heap_page_items(get_raw_page("test", 0));
  16. 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
  17. ----+--------+----------+--------+----------+--------+----------+--------+-------------+------------+--------+--------+-------
  18. 1 | 8160 | 1 | 30 | 75066031 | 0 | 0 | (0,1) | 2 | 2818 | 24 | |
  19. 2 | 8128 | 1 | 31 | 75066031 | 0 | 0 | (0,2) | 2 | 2818 | 24 | |
  20. 3 | 8096 | 1 | 32 | 75066031 | 0 | 0 | (0,3) | 2 | 2818 | 24 | |
  21. 4 | 8064 | 1 | 31 | 75066040 | 0 | 1 | (0,4) | 2 | 2818 | 24 | |
  22. 5 | 8032 | 1 | 32 | 75066040 | 0 | 2 | (0,5) | 2 | 2818 | 24 | |
  23. 6 | 8000 | 1 | 30 | 75066110 | 0 | 0 | (0,6) | 2 | 11010 | 24 | |
  24. (6 rows)
  25. t1=# update test set value = "c" where id = 4;
  26. UPDATE 1
  27. t1=# SELECT * FROM heap_page_items(get_raw_page("test", 0));
  28. 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
  29. ----+--------+----------+--------+----------+----------+----------+--------+-------------+------------+--------+--------+-------
  30. 1 | 8160 | 1 | 30 | 75066031 | 0 | 0 | (0,1) | 2 | 2818 | 24 | |
  31. 2 | 8128 | 1 | 31 | 75066031 | 0 | 0 | (0,2) | 2 | 2818 | 24 | |
  32. 3 | 8096 | 1 | 32 | 75066031 | 0 | 0 | (0,3) | 2 | 2818 | 24 | |
  33. 4 | 8064 | 1 | 31 | 75066040 | 0 | 1 | (0,4) | 2 | 2818 | 24 | |
  34. 5 | 8032 | 1 | 32 | 75066040 | 0 | 2 | (0,5) | 2 | 2818 | 24 | |
  35. 6 | 8000 | 1 | 30 | 75066110 | 75066121 | 0 | (0,7) | 16386 | 8962 | 24 | |
  36. 7 | 7968 | 1 | 30 | 75066121 | 0 | 0 | (0,7) | 32770 | 10242 | 24 | |
  37. (7 rows)
  38. t1=# select cmin,cmax,xmin,xmax,ctid, * from test;
  39. cmin | cmax | xmin | xmax | ctid | id | value
  40. ------+------+----------+------+-------+----+-------
  41. 0 | 0 | 75066031 | 0 | (0,1) | 1 | a
  42. 0 | 0 | 75066031 | 0 | (0,2) | 2 | aa
  43. 0 | 0 | 75066031 | 0 | (0,3) | 3 | aaa
  44. 1 | 1 | 75066040 | 0 | (0,4) | 5 | bb
  45. 2 | 2 | 75066040 | 0 | (0,5) | 6 | bbb
  46. 0 | 0 | 75066121 | 0 | (0,7) | 4 | c
  47. (6 rows)
  48. t1=# select txid_current();
  49. txid_current
  50. --------------
  51. 75066121
  52. (1 row)

Vacuum internals 文章实在太好了
Postgres Hint Bits

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/38954.html

相关文章

  • 关系型数据库中的事务管理详解:并发控制与事务日志

    摘要:关系型数据库中的事务管理详解并发控制与事务日志数据库系统的萌芽出现于年代。并发控制并发控制旨在针对数据库中对事务并行的场景,保证中的一致性与隔离性。绝大部分数据库会采用锁或者数据版本控制的方式来处理并发控制问题。 本文节选自:关系型数据库理论 https://url.wx-coder.cn/DJNQn ,涉及引用/整理的文章列举在了 Database-List。 showImg(htt...

    Pink 评论0 收藏0
  • 关系型数据库中的事务管理详解:并发控制与事务日志

    摘要:关系型数据库中的事务管理详解并发控制与事务日志数据库系统的萌芽出现于年代。并发控制并发控制旨在针对数据库中对事务并行的场景,保证中的一致性与隔离性。绝大部分数据库会采用锁或者数据版本控制的方式来处理并发控制问题。 本文节选自:关系型数据库理论 https://url.wx-coder.cn/DJNQn ,涉及引用/整理的文章列举在了 Database-List。 showImg(htt...

    haobowd 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<