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

1_PostgreSQL深入解析heap表物理结构

1_PostgreSQL深入解析heap表物理结构

1、内容概述

本文以psql (PostgreSQL) 15beta1 为分析对象,解析heap表物理结构,在pg数据库中 heap表 数据文件以 pages为单位划分为定长的单元,默认为8192 byte (8 KB),pages编号0开始递增,pages的编号 称为block number,pages中被数据填充满后,pg通过在文件的末尾增加新的page,扩展数据文件的大小。

小结:

1、pg中的pages又称为block,类似Oracle中的blocks,Oracke中的block默认大小也为8K.

2、把datafile中的物理存储单位称为pages,后面的pages编号称为block number,概念存在不连贯性,可能由于历史原因有不同的概念定义,block 和后面的block number连贯性更好点,便于理解。

3、page扩展时是一次扩展一个page还是一次扩展多个,需要后面实验验证,Oracle中是以extent进行扩展,扩展规则如下所示:

BLOCKS   COUNT(1)
---------- ----------
  8     16
128      63
1024    120
8192      ??? --这个阀值后面没有再测试

2、创建测试表

[postgres@pg ~]$ psql psql (15beta1) Type "help" for help. postgres=# create table test(a1 char(10)); CREATE TABLE postgres=# SELECT pg_relation_filepath('test'); pg_relation_filepath ---------------------- base/5/32793 (1 row) postgres=#

3、当Tuples=0(0行数据)时

[postgres@pg 5]$ ls -ltr|grep 32793 -rw------- 1 postgres dba 0 Jun 20 11:26 32793 [postgres@pg 5]$ dd if=/u01/pg15/pgdata/base/5/32793 bs=8192 skip=0 count=1 | od -t x1 0+0 records in 0+0 records out 0 bytes (0 B) copied, 2.5415e-05 s, 0.0 kB/s 0000000 [postgres@pg 5]$

小结:

1、pg在创建heap表后会创建一个大小为0 byte的空文件,没有初始化任何块及段头块信息。

2、Oracle表默认在创建后会初始化第一个extent,大小为8个blocks,前3个块为位图块,后面5个块为未初始化的数据块。

4、当Tuples=1(1行数据)时

4.1、插入第1行数据库

postgres=# insert into test values(1); INSERT 0 1 postgres=# [postgres@pg 5]$ ls -ltr|grep 32793 -rw------- 1 postgres dba 8192 Jun 20 12:00 32793 [postgres@pg 5]$

4.2、以16进程方式读取块信息 dd+od VS dd+hexdump

以dd+od方式读取二进制文件的块信息,并以16进制显示,可以看到左边的行号显示有误。

image.png

以dd+hexdump方式读取二进制文件的块信息,并以16进制显示,左边的行号显示正常。 这里的 0x2000(16进制) = 8192(10进制),8192为一个pg page的默认大小。

image.png

小结:

1、将插入第一行数据,pg数据文件从0 byte初始化为8192 byte。

2、对于二进制文件内容的读取常用的方式有 dd+od和 dd+hexdump二种方式,dd+od的方式左边的行号显示有误,容易产生误解,我们后面均采用dd+hexdump的方式。

3、在C语言中以 0x或0X表示16进制,10进制前没有前缀符号

4、信息系统中的不 同进制表示法,还有以下标法进行区分,以 8192为例:

二进制:0010 0000 0000 00002

十进制:819210

十六进制:200016

4.3、安装pageinspect扩展查看页头信息

su - postgres cd /soft/postgresql-15beta1/contrib/pageinspect [postgres@pg pageinspect]$ make && make install postgres=# create extension pageinspect; CREATE EXTENSION postgres=# [postgres@pg pageinspect]$ psql psql (15beta1) Type "help" for help. postgres=# create extension pageinspect; CREATE EXTENSION postgres=# SELECT * FROM page_header(get_raw_page('test', 0)); lsn | checksum | flags | lower | upper | special | pagesize | version | prune_xid ------------+----------+-------+-------+-------+---------+----------+---------+----------- 1/868D38F0 | 0 | 0 | 28 | 8152 | 8192 | 8192 | 4 | 0 (1 row) postgres=#

小结:

  1. 查看PageHeader信息前需要安装 pageinspect 扩展,详细用法请参见以下文档:

    https://www.postgresql.org/docs/15/pageinspect.html

  2. 《pg pageinspect扩展用法讲解》 后面单独一节详细讲解

  3. 《深入解析pg lsn》后面单独一节详细讲解

4.4、解析pg page物理结构

[postgres@pg 5]$ dd if=/u01/pg15/pgdata/base/5/32793 bs=8192 skip=0 count=1 | hexdump -C 1+0 records in 1+0 records out 8192 bytes (8.2 kB) copied, 4.712e-05 s, 174 MB/s 00000000 01 00 00 00 f0 38 8d 86 00 00 00 00 1c 00 d8 1f |.....8..........| 00000010 00 20 04 20 00 00 00 00 d8 9f 46 00 00 00 00 00 |. . ......F.....| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00001fd0 00 00 00 00 00 00 00 00 03 03 00 00 00 00 00 00 |................| 00001fe0 00 00 00 00 00 00 00 00 01 00 01 00 02 08 18 00 |................| 00001ff0 17 31 20 20 20 20 20 20 20 20 20 00 00 00 00 00 |.1 .....| 00002000 [postgres@pg 5]$

4.5、图解PageHeader物理结构

image.png

小结:

1、char(10)的一个Tuple占用长度10 bytes,《深入解析Tuple物理结构》单独一节详解

5、当Tuples=2(2行数据)时

5.1、插入第2行数据库

postgres=# insert into test values(1); INSERT 0 1 postgres=# [postgres@pg 5]$ ls -ltr|grep 32793 -rw------- 1 postgres dba 8192 Jun 20 12:00 32793 [postgres@pg 5]$

5.2、解析pg page物理结构

[postgres@pg 5]$ dd if=/u01/pg15/pgdata/base/5/32793 bs=8192 skip=0 count=1 | hexdump -C 1+0 records in 1+0 records out 8192 bytes (8.2 kB) copied, 6.1767e-05 s, 133 MB/s 00000000 01 00 00 00 a8 f3 92 86 00 00 00 00 20 00 b0 1f |............ ...| 00000010 00 20 04 20 00 00 00 00 d8 9f 46 00 b0 9f 46 00 |. . ......F...F.| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00001fb0 05 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00001fc0 02 00 01 00 02 09 18 00 17 31 20 20 20 20 20 20 |.........1 | 00001fd0 20 20 20 00 00 00 00 00 03 03 00 00 00 00 00 00 | .............| 00001fe0 00 00 00 00 00 00 00 00 01 00 01 00 02 09 18 00 |................| 00001ff0 17 31 20 20 20 20 20 20 20 20 20 00 00 00 00 00 |.1 .....| 00002000 [postgres@pg 5]$

5.3、解析line pointer信息

image.png

6、line pointer结构解析问题

6.1、PageHeaderData结构源码

typedef struct PageHeaderData { /* XXX LSN is member of *any* block, not only page-organized ones */ XLogRecPtr pd_lsn; /* LSN: next byte after last byte of xlog * record for last change to this page */ uint16 pd_tli; /* least significant bits of the TimeLineID * containing the LSN */ uint16 pd_flags; /* flag bits, see below */ LocationIndex pd_lower; /* offset to start of free space */ LocationIndex pd_upper; /* offset to end of free space */ LocationIndex pd_special; /* offset to start of special space */ uint16 pd_pagesize_version; TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */ ItemIdData pd_linp[1]; /* beginning of line pointer array */ } PageHeaderData; typedef PageHeaderData *PageHeader;

6.2、ItemIdData结构源码

/* * An item pointer (also called line pointer) on a buffer page * * In some cases an item pointer is "in use" but does not have any associated * storage on the page. By convention, lp_len == 0 in every item pointer * that does not have storage, independently of its lp_flags state. */ typedef struct ItemIdData { unsigned lp_off:15, /* offset to tuple (from start of page) */ lp_flags:2, /* state of item pointer, see below */ lp_len:15; /* byte length of tuple */ } ItemIdData; typedef ItemIdData *ItemId;

6.3、pd_linp信息图解

image.png

6.4、C语言位域结构说明

ItemIdData结构为C语言的位域结构体,该结构的定义从 低位到高位,即结构体中的 lp_len->lp_flags->lp_off为从高位到低的的顺序。

7、尝试产生第二个page

​ 当第一个page填满数据后,pg在数据文件末尾产生第二个8k大小的page,数据文件从源来的8K扩展为16K。

postgres=# insert into test select n from generate_series(1,230) as n; INSERT 0 230 postgres=# [postgres@pg 5]$ ls -ltr|grep 32793 -rw------- 1 postgres dba 24576 Jun 20 21:52 32793_fsm -rw------- 1 postgres dba 16384 Jun 20 21:52 32793 [postgres@pg 5]$

小结:

思考问题一、以上只测试了当第一个page满后,pg只扩展一个page到数据文件的末尾,随着数据文件的扩大是否一直每次只扩展一个page,留做一个问题,我们后面单独一节分析

8、关于 checksum 字段的问题

8.1、PageHeader源码结构

typedef struct PageHeaderData { /* XXX LSN is member of *any* block, not only page-organized ones */ XLogRecPtr pd_lsn; /* LSN: next byte after last byte of xlog * record for last change to this page */ uint16 pd_tli; /* least significant bits of the TimeLineID * containing the LSN */ uint16 pd_flags; /* flag bits, see below */ LocationIndex pd_lower; /* offset to start of free space */ LocationIndex pd_upper; /* offset to end of free space */ LocationIndex pd_special; /* offset to start of special space */ uint16 pd_pagesize_version; TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */ ItemIdData pd_linp[1]; /* beginning of line pointer array */ } PageHeaderData; typedef PageHeaderData *PageHeader;

8.2、查询页头信息

postgres=# SELECT * FROM page_header(get_raw_page('test', 0)); lsn | checksum | flags | lower | upper | special | pagesize | version | prune_xid ------------+----------+-------+-------+-------+---------+----------+---------+----------- 1/868D38F0 | 0 | 0 | 28 | 8152 | 8192 | 8192 | 4 | 0 (1 row) postgres=#

小结:

查询页头信息中 有 checksum字段,但在源码 PageHeaderData结构体中没有该字段对应位置为

uint16 pd_tli; /* least significant bits of the TimeLineID

《checksum与pd_tli问题分析》会做新单独一节进行分析

9、参考资料

9.1、源码信息

说明:

The structure PageHeaderData is defined in src/include/storage/bufpage.h.

9.2、blob信息

http://www.interdb.jp/pg/pgsql01.html

image.png

最后修改时间:2022-10-31 08:50:16
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论