暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

用pageinspect浅探下PostgreSQL的MVCC

呆呆的私房菜 2024-07-30
29
    Whoami:5年+金融、政府、医疗领域工作经验的DBA
    Certificate:OCP、PCP
    Skill:Oracle、Mysql、PostgreSQL
    Platform:CSDN、墨天伦、公众号(呆呆的私房菜)
    复制



    阅读本文可以了解PostgreSQL的MVCC概念和实现。




    01

    MVCC概述
    • 到底什么是MVCC呢?

    • 1. MVCC即多版本并发控制,是一种用于处理数据库中并发操作的机制;

    • 2. 在MVCC中,每个数据库事务在读取数据时会看到一个特定的版本,这使得事务之间可以同时进行读写操作,并且不会相互干扰。每个事务可以操作自己的数据版本,从而实现了更高的并发性和更好的性能;

    • 3. MVCC核心思想是对每个修改此操作,不是直接在原始数据上进行修改,而是创建一个新的数据版本,并将修改操作应用于新版本,这样其它事务仍可以访问旧版本数据,而不会受到正在进行的修改的影响。只有在事务提交时,新版本的数据才会替代旧版本,从而实现数据的一致性。

    • 4. MVCC在数据库中起作用的原理是通过使用版本号、时间戳或类似的标识来管理数据版本,并通过一定的规则来决定哪些版本对于每个事务是可见的。


    02

    MVCC实现方法
    • 第一种:写新数据时,元数据移到一个单独的位置,如回滚段中,其他用户读数据时,从回滚段中把元数据读出来。

    • 第二种:写新数据时,原数据不删除,而是把新数据插入进来。


    03

    PostgreSQL MVCC
    • PostgreSQL数据库是通过把原数据保留在数据文件中,新插入一条数据来实现多版本并发功能的。

    • 我们可以通过pageinspect插件来窥视PostgreSQL的多版本控制工作过程。

      ## 为实现MVCC,PostgreSQL数据库的每张表上都存在4个系统字段:
      ## 1. xmin:表示记录的第一个可见事务的 XID;
      ## 2. xmax:表示记录的最后一个修改它的事务的 XID;
      ## 3. cmin:表示命令ID的最小值;
      ## 4. cmax:表示命令ID的最大值。


      postgres=# create extension pageinspect;
      CREATE EXTENSION


      postgres=# create table a1 (id int);
      CREATE TABLE


      postgres=# insert into a1 values (1),(2);
      INSERT 0 2


      ## 不收集统计信息前,看不到当前元组使用的数据页和元组数量
      postgres=# select relname, relpages, reltuples from pg_class where relname = 'a1';
      relname | relpages | reltuples
      ---------+----------+-----------
      a1 | 0 | -1
      (1 row)


      ## 收集统计信息
      postgres=# analyze a1;
      ANALYZE


      ## 表a1占用了1个数据页,一共有2个元组
      testdb=# select relname, relpages, reltuples from pg_class where relname = 'a1';
      relname | relpages | reltuples
      ---------+----------+-----------
      a1 | 1 | 2
      (1 row)


      ## heap_page_items函数用于获取数据库堆表页上条目的信息
      ## lp: 指向下一条记录的指针。如果这是页上的最后一条记录,这个值通常是指向页末尾的偏移量。
      ## lp_off: 记录在页上的偏移量。
      ## lp_flags: 记录的一些状态标志,比如是否是脏页(dirty)。
      ## lp_len: 记录的长度。
      ## t_xmin: 插入这条记录的事务的最小事务 ID(XID)。如果这个值是 InvalidTransactionId(0),表示这条记录是可见的。
      ## t_xmax: 最后一次删除或更新这条记录的事务的事务 ID。如果这个值是 0,表示这条记录没有被当前事务删除或更新。
      ## t_field3: 这个字段通常用于存储额外的事务信息,但具体内容取决于 PostgreSQL 的版本。
      ## t_ctid: 当前记录的复合索引,包含块号和行号。
      ## t_infomask2: 一个掩码,用于存储记录的额外信息,比如是否是只读副本。
      ## t_infomask: 另一个掩码,用于存储记录的元数据信息,比如是否是压缩的或是否包含 OID。
      ## t_hoff: 指向记录头信息的偏移量。
      ## t_bits: 一个位图,用于存储记录的可见性和其他信息。
      ## t_oid: 如果记录包含 OID 列,则这个字段存储该值。
      ## t_data: 记录的实际数据,通常以字节序列的形式存储。
      ## 输出解读:
      ## 1. 两条记录的t_xmin,说明是由同个事务插入的
      ## 2. t_xmax没有记录代表没有事务更新或删除这条记录
      ## 3. t_ctid显示了记录的当前位置
      ## postgres=# select * from heap_page_items(get_raw_page('a1',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 | t_data
      ----+--------+----------+--------+------------+--------+----------+--------+-------------+------------+--------+--------+-------+------------
      1 | 8160 | 1 | 28 | 2146483941 | 0 | 0 | (0,1) | 1 | 2304 | 24 | | | \x01000000
      2 | 8128 | 1 | 28 | 2146483941 | 0 | 0 | (0,2) | 1 | 2304 | 24 | | | \x02000000
      (2 rows)


      ## 更新一条数据
      postgres=# update a1 set id = 22 where id = 2;
      UPDATE 1


      ## 输出解读:
      ## 1. 新增了一条xid为2146483943的记录,并且记录了更新后的数据
      ## 2. 被更新的记录并没有被删除,而是被标记为旧版本,数据仍保存在原数据文件中。
      postgres=# select * from heap_page_items(get_raw_page('a1',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 | t_data
      ----+--------+----------+--------+------------+------------+----------+--------+-------------+------------+--------+--------+-------+------------
      1 | 8160 | 1 | 28 | 2146483941 | 0 | 0 | (0,1) | 1 | 2304 | 24 | | | \x01000000
      2 | 8128 | 1 | 28 | 2146483941 | 2146483943 | 0 | (0,3) | 16385 | 256 | 24 | | | \x02000000
      3 | 8096 | 1 | 28 | 2146483943 | 0 | 0 | (0,3) | 32769 | 10240 | 24 | | | \x16000000
      (3 rows)
      复制
      • 动动手,做做试验吧!通过上边小小的实验我们就可以看到PostgreSQL的多版本并发控制的实现原理啦!



      本文内容就到这啦,阅读完本篇,相信你也对PostgreSQL的MVCC实现有了初步的了解了吧!我们下篇再见!

      点击上方公众号,关注我吧!


      文章转载自呆呆的私房菜,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

      评论