03-PG存储——Page和Tuple

1.PAGE

1.1 page 结构

每个page(也叫block)包含page header、Iterm Pointers、free space、tuples、special space 。

 * +----------------+---------------------------------+
 * | PageHeaderData | linp1 linp2 linp3 ...           |
 * +-----------+----+---------------------------------+
 * | ... linpN |                                      |
 * +-----------+--------------------------------------+
 * |           ^ pd_lower                             |
 * |                                                  |
 * |                     v pd_upper                   |
 * +-------------+------------------------------------+
 * |                     | tupleN ...                  |
 * +-------------+------------------+-----------------+
 * |       ... tuple3 tuple2 tuple1 | "special space" |
 * +--------------------------------+-----------------+
 *                                  ^ pd_special
 

上面就是/soft/postgresql-14.6/src/include/storage/bufpage.h 中给出的page页结构图,我们也可以使用pageinspect插件查看PostgreSQL中的页结构

postgres=# create extension pageinspect;  --创建pageinspect扩展
CREATE EXTENSION
postgres=# create table t1(id int,name varchar(10));
CREATE TABLE
postgres=# SELECT lower, upper, special, pagesize
postgres-# FROM page_header(get_raw_page('t1',0));
ERROR:  block number 0 is out of range for relation "t1"   --未插入数据,此时没有分配page
postgres=# insert into t1 values(1,'aaa');
INSERT 0 1
postgres=# commit;
postgres=# SELECT lower, upper, special, pagesize
postgres-# FROM page_header(get_raw_page('t1',0));
 lower | upper | special | pagesize 
-------+-------+---------+----------
    28 |  8160 |    8192 |     8192
(1 row)

根据上面查询,可以大致知道t1表的第一个块的结构:

  • 0-23 位,24字节 pgheader
  • 24-27 位 4 字节 item pointer 1,也成linp1 就是第一个tuple(行)指针
  • 28位(低位)到8160位 (高位)是空闲空间。
  • 8161到8192为第一个tuple位置,也就是行记录,一行占32个字节

8192标记为特殊空间,它可以包含访问方法想存放的任何东西。比如,b-tree 索引用它存储指向页面的左右兄妹的链接,以及其他一些和索引结构相关的数据。普通表并不使用这个部分。

如果我们再查如一行记录,lower应该变为32,upper应该变为8128,下面验证

postgres=# insert into t1 values(2,'bbb');
INSERT 0 1
postgres=# SELECT lower, upper, special, pagesize
FROM page_header(get_raw_page('t1',0));
 lower | upper | special | pagesize 
-------+-------+---------+----------
    32 |  8128 |    8192 |     8192
(1 row)

1.2 PAGE HEADER

Page Header的结构在bufpage.h的PageHeaderData结构体定义,主要包含以下内容:

  • pd_lsn(8位):记录此页面的最后修改的xlog 记录
  • pd_checksum(2位):页面校验码
  • pd_flag(2):标志位
  • pd_lower(2):空闲空间开头偏移量
  • pd_upper(2):空闲空间结尾偏移量
  • pd_special(2):特殊空间开头偏移量
  • pd_pagesize_version(2):页面大小和版本号
  • pd_prune_xid(4):页面最老未删除的xmax,如果没有则为0

上述信息都可以通过pageinspect查看到

postgres=# SELECT * FROM page_header(get_raw_page('t1',0));
    lsn    | checksum | flags | lower | upper | special | pagesize | version | prune_xid 
-----------+----------+-------+-------+-------+---------+----------+---------+-----------
 0/308BB20 |        0 |     0 |    32 |  8128 |    8192 |     8192 |       4 |         0
(1 row)

2.TUPLE

2.1 结构

所有的的表的tuple结构都是一致的。包含定长的header(一般为23字节),可选的空值位图,可选的对象ID域和用户数据。

tuple header的结构如下:

t_xmin(4):插入xid标志,在创建(insert)记录(tuple)时,记录此值为插入tuple的事务ID

t_xmax(4):删除xid标志,默认值为0,在删除tuple时,记录此值

t_cid(4):插入和删除cid标志(覆盖t_xvac)

t_xvac(4):vacuum操作移动一个行版本的xid

t_ctid(6):当前版本的TID或者指向更新的行版本

t_infomask2(2):一些属性,加上多个标志位

t_infomask(2):多个标志位

t_hoff(1):到用户数据的偏移量

我们可以通过heap_page_items函数获取上面信息:

postgres=# drop table t1;
DROP TABLE
postgres=# create table t1 (id int,name varchar(10));
CREATE TABLE
postgres=# begin;    --第一个事务
BEGIN
postgres=*# select txid_current();
 txid_current 
--------------
      1048600
(1 row)

postgres=*# insert into t1 values(1,'aaa');
INSERT 0 1
postgres=*# insert into t1 values(2,'bbb');
INSERT 0 1
postgres=*# commit;
COMMIT
postgres=# begin;   --第二个事务
BEGIN
postgres=*# select txid_current();
 txid_current 
--------------
      1048601
(1 row)

postgres=*# insert into t1 values(3,'ccc');
INSERT 0 1
postgres=*# insert into t1 values(4,'ddd');
INSERT 0 1
postgres=*# insert into t1 values(5,'eee');
INSERT 0 1
postgres=*# commit;
COMMIT
postgres=# select lp as linpn,
postgres-#    lp_flags,t_xmin,t_xmax,
postgres-#    t_field3 as t_cid,
postgres-#    t_ctid,t_infomask2,t_infomask,t_hoff
postgres-# from heap_page_items(get_raw_page('t1',0));
 linpn | lp_flags | t_xmin  | t_xmax | t_cid | t_ctid | t_infomask2 | t_infomask | t_hoff 
-------+----------+---------+--------+-------+--------+-------------+------------+--------
     1 |        1 | 1048600 |      0 |     0 | (0,1)  |           2 |       2050 |     24
     2 |        1 | 1048600 |      0 |     1 | (0,2)  |           2 |       2050 |     24
     3 |        1 | 1048601 |      0 |     0 | (0,3)  |           2 |       2050 |     24
     4 |        1 | 1048601 |      0 |     1 | (0,4)  |           2 |       2050 |     24
     5 |        1 | 1048601 |      0 |     2 | (0,5)  |           2 |       2050 |     24
(5 rows)

  • lp代表元组的序号。
  • lp_flags ,代表元组的状态,0-unused,1-normal,2-redirect,4-dead。
  • t_xmin为1048600,代表插入此元组的事务。
  • t_xmax为 0 代表此事务尚未被删除或更新;
  • t_cid代表是元组为1048600第一个SQL命令插入的;t_ctid为(0,1)指向自身,因为该元组为最新版本。前面的0为page序号,后面的1为偏移量(offset)取自tuple序号。
  • t_infomask2
  • t_infomask :t_infomask & 256 >0 xmin_committed ,t_infomask & 512>0 xmin_aborted,t_infomask & 1024 >0 xmax_committed ,t_infomask & 2048 xmax_aborted
  • t_hoff代表用户数据的偏移量.

上面只描述了普通表元组结构,Index 元组和TOAST元组将在后续文章中介绍

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南風_入弦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值