前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何从零学习PostgreSQL Page结构

如何从零学习PostgreSQL Page结构

作者头像
数据和云
发布2018-12-25 16:54:59
1K0
发布2018-12-25 16:54:59
举报
文章被收录于专栏:数据和云数据和云

作者 | 李亮,云和恩墨西区交付工程师,长期服务于运营商、社保、银行、医院、公积金等行业,擅长数据库备份恢复,升级迁移,性能优化,sql优化。

导读:PostgreSQL 号称是“世界上最先进的开源数据库”(The world's most advanced open source database),在DB-Engines的排名中长期处于第四的位置,并且增长趋势明显。开源的优势就是可以直接阅读源码,本文通过源码结合pageinspcet对pg的page结构进行解析和学习。

一、Page

pg中的page和Oracle中的数据块一样,指的是数据库的块,操作系统块的整数倍个,默认是8K也就是两个操作系统块(4k的文件系统块)。这个大小在pg编译安装configure的时候通过--with-blocksize参数指定,单位是Kb。

二、Page的内部结构

2.1 page结构

2.2 PageHeaderData数据结构 (页头)

可以看到一个Page有 Pager header(页头),后面是linp(行指针),pd_lower和pd_upper分别是空闲空间的开始位置和结束位置;后面就是行数据(pg里面的行就是tuple)和special空间。整个page的结构比Oracle的数据块结构简单多了。

typedef struct PageHeaderData{ /* XXX LSN is member of *any* block, not only page-organized ones */ PageXLogRecPtr pd_lsn; /* LSN: next byte after last byte of xlog * record for last change to this page */ uint16 pd_checksum; /* checksum */ 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[FLEXIBLE_ARRAY_MEMBER]; /* line pointer array */} PageHeaderData;

具体的长度和描述也都有详细说明:

Field

Type

Length

Description

pd_lsn

PageXLogRecPtr

8 bytes

LSN: next byte after last byte of WAL record for last change to this page

pd_checksum

uint16

2 bytes

Page checksum

pd_flags

uint16

2 bytes

Flag bits

pd_lower

LocationIndex

2 bytes

Offset to start of free space

pd_upper

LocationIndex

2 bytes

Offset to end of free space

pd_special

LocationIndex

2 bytes

Offset to start of special space

pd_pagesize_version

uint16

2 bytes

Page size and layout version number information

pd_prune_xid

TransactionId

4 bytes

Oldest unpruned XMAX on page, or zero if none

简单来说,pd_lsn是指最后修改过这个page的lsn(log sequence number),这个和wal(write ahead log,同oracle redo)中记录的lsn一致。数据落盘时redo必须先刷到wal,这个pd_lsn就记录了最后data落盘时的相关redo的lsn。

pd_checksum是校验和,在initdb初始化实例的时候通过-k参数指定开启,默认是关闭的,initdb之后不能修改,它基于FNV-1a hash算法,做了相应的更改。这个校验和与Oracle的checksum一样用于数据块在读入和写出内存时的校验。比如我们在内存中修改了一个数据块,写入到磁盘的时候,在内存里面先计算好checksum,数据块写完后再计算一遍cheksum是否和之前在内存中的一致,确保整个写出过程没有出错,保护数据结构不被破坏。

pd_flags有以下的值:

/* * pd_flags contains the following flag bits. Undefined bits are initialized * to zero and may be used in the future. * * PD_HAS_FREE_LINES is set if there are any LP_UNUSED line pointers before * pd_lower. This should be considered a hint rather than the truth, since * changes to it are not WAL-logged. * * PD_PAGE_FULL is set if an UPDATE doesn't find enough free space in the * page for its new tuple version; this suggests that a prune is needed. * Again, this is just a hint. */#define PD_HAS_FREE_LINES 0x0001 /* are there any unused line pointers? */#define PD_PAGE_FULL 0x0002 /* not enough free space for new tuple? */#define PD_ALL_VISIBLE 0x0004 /* all tuples on page are visible to * everyone */ #define PD_VALID_FLAG_BITS 0x0007 /* OR of all valid pd_flags bits */

pd_lower和pd_upper分别表示空闲空间起始位置和结束位置;pd_special在索引page才有效;pd_pagesize_version是page大小和page version的存储位,在不同数据库版本中,page version不一样:

数据库版本

pd_pagesize_version

<7.3

0

7.3 & 7.4

1

8.0

2

8.1

3

>8.3

4

prune_xid表示这个page上最早删除或者修改tuple的事务id,在vacuum操作的时候会用到。(pg没有undo,旧的数据也在page中,用vacuum来清理)

2.3 linp结构(行指针)

lp_off是tuple的开始的偏移量;lp_flags是标志位;lp_len记录了tuple的长度。

Field

Length

Description

lp_off

15 bits

offset to tuple

lp_flags

2 bits

State of iteam pointer

lp_len

15 bits

Byte length of tuple

2.4 tuple header结构(行头)

typedef struct HeapTupleFields{ TransactionId t_xmin; /* inserting xact ID */ TransactionId t_xmax; /* deleting or locking xact ID */ union { CommandId t_cid; /* inserting or deleting command ID, or both */ TransactionId t_xvac; /* old-style VACUUM FULL xact ID */ } t_field3;} HeapTupleFields; typedef struct DatumTupleFields{ int32 datum_len_; /* varlena header (do not touch directly!) */ int32 datum_typmod; /* -1, or identifier of a record type */ Oid datum_typeid; /* composite type OID, or RECORDOID */ /* * Note: field ordering is chosen with thought that Oid might someday * widen to 64 bits. */} DatumTupleFields; struct HeapTupleHeaderData{ union { HeapTupleFields t_heap; DatumTupleFields t_datum; } t_choice; ItemPointerData t_ctid; /* current TID of this or newer tuple (or a * speculative insertion token) */ /* Fields below here must match MinimalTupleData! */ uint16 t_infomask2; /* number of attributes + various flags */ uint16 t_infomask; /* various flag bits, see below */ uint8 t_hoff; /* sizeof header incl. bitmap, padding */ /* ^ - 23 bytes - ^ */ bits8 t_bits[FLEXIBLE_ARRAY_MEMBER]; /* bitmap of NULLs */ /* MORE DATA FOLLOWS AT END OF STRUCT */};(*这部分代码在src/include/access/htup_details.h)

也有对应的长度和描述的相详细说明:

Field

Type

Length

Description

t_xmin

TransactionId

4 bytes

insert XID stamp

t_xmax

TransactionId

4 bytes

delete XID stamp

t_cid

CommandId

4 bytes

insert and/or delete CID stamp (overlays with t_xvac)

t_xvac

TransactionId

4 bytes

XID for VACUUM operation moving a row version

t_ctid

ItemPointerData

6 bytes

current TID of this or newer row version

t_infomask2

uint16

2 bytes

number of attributes, plus various flag bits

t_infomask

uint16

2 bytes

various flag bits

t_hoff

uint8

1 byte

offset to user data

union是共享结构体,起作用的变量是最后一次赋值的成员。来看看tuple header的结构。

在HeapTupleFields中,t_xmin是插入这行tuple的事务id;t_xmax是删除或者锁住tuple的事务id;union结构中的t_cid是删除或者插入这个tuple的命令id,也就是命令序号;t_xvac是以前格式的vacuum full用到的事务id。

在DatumTupleFields中,datum_len_ 指tuple的长度;datum_typmod是记录的type;datum_typeid是记录的id。

页头HeapTupleHeaderData包含了union结构体中的两个变量HeapTupleFields和DatumTupleFields。t_ctid是tuple id,类似oracle的rowid,形式为(块号,行号)。

t_infomask2 表示属性和标志位

t_infomask 是flag标志位,具体值如下:

/* * information stored in t_infomask: */#define HEAP_HASNULL 0x0001 /* has null attribute(s) */#define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */#define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */#define HEAP_HASOID 0x0008 /* has an object-id field */#define HEAP_XMAX_KEYSHR_LOCK 0x0010 /* xmax is a key-shared locker */#define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */#define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */#define HEAP_XMAX_LOCK_ONLY 0x0080 /* xmax, if valid, is only a locker */ /* xmax is a shared locker */#define HEAP_XMAX_SHR_LOCK (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK) #define HEAP_LOCK_MASK (HEAP_XMAX_SHR_LOCK | HEAP_XMAX_EXCL_LOCK | \ HEAP_XMAX_KEYSHR_LOCK)#define HEAP_XMIN_COMMITTED 0x0100 /* t_xmin committed */#define HEAP_XMIN_INVALID 0x0200 /* t_xmin invalid/aborted */#define HEAP_XMIN_FROZEN (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)#define HEAP_XMAX_COMMITTED 0x0400 /* t_xmax committed */#define HEAP_XMAX_INVALID 0x0800 /* t_xmax invalid/aborted */#define HEAP_XMAX_IS_MULTI 0x1000 /* t_xmax is a MultiXactId */#define HEAP_UPDATED 0x2000 /* this is UPDATEd version of row */#define HEAP_MOVED_OFF 0x4000 /* moved to another place by pre-9.0 * VACUUM FULL; kept for binary * upgrade support */#define HEAP_MOVED_IN 0x8000 /* moved from another place by pre-9.0 * VACUUM FULL; kept for binary * upgrade support */#define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN) #define HEAP_XACT_MASK 0xFFF0 /* visibility-related bits */

t_hoff表示tuple header的长度

t_bits记录了tuple中null值的列

三、实验

3.1 安装pageinspect

它在源码的crontrib目录下面

postgres@cs-> cd postgresql-10.4/contrib/pageinspect make && make installpostgres@cs-> makepostgres@cs-> make install create extension就好了postgres@cs-> psqlpsql (10.4)Type "help" for help. postgres=# CREATE EXTENSION pageinspect;CREATE EXTENSION postgres=# \xExpanded display is on.postgres=# \dxList of installed extensions-[ RECORD 1 ]------------------------------------------------------Name | pageinspectVersion | 1.6Schema | publicDescription | inspect the contents of database pages at a low level-[ RECORD 2 ]------------------------------------------------------Name | plpgsqlVersion | 1.0Schema | pg_catalogDescription | PL/pgSQL procedural language

3.2 创建建测试表t1,插入数据

这里可以看到1000行数据用了6个数据块来存储(这里数据块从0开始),第6个数据块包含了73条记录(tuple)

3.3 Pageinspect查看page

这里我们通过两个函数来查看

page_header 可以看到页头的数据

heap_pageitems 可以看到具体tuple的数据

3.3.1 page_header

postgres=# \xExpanded display is on.postgres=# select * from page_header(get_raw_page('t1',0));-[ RECORD 1 ]--------lsn | 0/1671188checksum | 0flags | 0lower | 772upper | 784special | 8192pagesize | 8192version | 4prune_xid | 0postgres=#

可以看到第0个page的pd_lsn为0/1671188,checksum和flags都是0,这里没有开启checksum;tuple开始偏移是772(pd_lower),结束偏移是784(pd_upper),这个page是个表,所以它没有special,我们看到的sepcial就是8192了;pagesize是8192就是8K,version是4,没有需要清理的tuple,所以存储需要清理的tuple的最早事务的id就是0(prune_xid)。

3.3.2 heap_page_items

我们来看一行记录,可以看到它是第1行记录(lp=1),tuple的开始偏移量8160(lp_off),tuple的长度是32 bytes(lp_len为32,这个tuple是第一个插入的tuple,所以lp_off+lp_len=8160+32=8192),这行记录的插入事务id是557(t_min),和tuple的删除事务id是0(tmax),这里数据没有被删除,所以都是0。我们还可以看到t_ctid是(0,1),这里表示这个tuple是这个page中第一个块的第一条tuple;tinfomask2是2,t_infomask为2306,十六进制就是 0x0902 ,这个我们可以根据上面提到的值去看看具体的含义,0x0902 = 0x0100 + 0x0800 +0x0002;tuple头部结构(行头)的长度是24(t_hoff),t_data就是16进制存储的真正的数据了。

3.4 删除一行数据观察prune_xid

我们删除一行tuple可以看到prune_xid有了值,为559,这个559就是删除这个tuple的事务id(当前最早的删除或更改了tuple的事务id)

同样,我们可以看到lp为1的这个tuple的t_xmax为559,这里就是删除这行tuple的事务id。

PostgreSQL Page的物理结构相比Oracle的数据块来说简单很多了,源代码开放也便于学习和研究,pg是个很好很强大的数据库,值得好好学习。

原创:李亮

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据和云 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Page
  • 二、Page的内部结构
    • 2.1 page结构
      • 2.3 linp结构(行指针)
        • 2.4 tuple header结构(行头)
        • 三、实验
          • 3.1 安装pageinspect
            • 3.2 创建建测试表t1,插入数据
              • 3.3 Pageinspect查看page
                • 3.3.1 page_header
                • 3.3.2 heap_page_items
              • 3.4 删除一行数据观察prune_xid
              相关产品与服务
              数据库备份服务
              数据库备份服务(Database Backup Service,简称 DBS)是为用户提供连续数据保护、低成本的备份服务。数据库备份拥有一套完整的数据备份和数据恢复解决方案,具备实时增量备份以及快速的数据恢复能力,它可以为多种部署形态的数据库提供强有力的保护,包括企业 IDC 数据中心、其他云厂商数据库及腾讯公有云数据库。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档