postgres Page介绍

文章目录

  • 前言
  • Page结构
  • pageHeaderData
  • 行指针
  • 空闲空间
  • tuple数据结构
  • special空间
  • 结尾

前言

本文是基于postgresql 13的代码进行分析解读,演示是在centos8系统上进行。


Page结构

 

  • 结构图

 

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

  • 结构说明

(1) PageHeaderData

(2) 行指针,也就是图中的linp1;占4字节,由offset ,flag,length, flag占2bytes;

(3)空闲空间;

(4)tuple数据

(5)special空间

2、PageHeaderData介绍

  • 数据结构

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;

typedef PageHeaderData *PageHeader;

  • 字段介绍

Pd_lsn :  最后修改对应WAL (Log Sequence Number )这个lsn也被称为PageLSN,它确定和记录了最后更改此页的xlog记录的LSN,把数据页和WAL日志关联,用于恢复数据时校验日志文件和数据文件的一致性

Pd_checksum:

Pd_flags:

取值有:

#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_lower指向空闲空间的起始位置,pd_upper指向空闲空间的结束位置;linp空间从pd_lower指向位置开始分配,并向后移动; tuple数据从pd_upper位置往前分配,分配后pd_upper向前移动。

Pd_special: special空间的起始位置,结束位置就是块大小

Pd_pagesize_version: 为了兼容以前版本,把pagesize和version放在了2字节中。低8bit是版本,高8bit是pagesize,规定pagesize是256的倍数;8bit表示的最大的pagesize是32KB;

pageSize获取方法:

#define PageGetPageSize(page) \

    ((Size) (((PageHeader) (page))->pd_pagesize_version & (uint16) 0xFF00))

version的取值如下:

/*

 * Page layout version number 0 is for pre-7.3 Postgres releases.

 * Releases 7.3 and 7.4 use 1, denoting a new HeapTupleHeader layout.

 * Release 8.0 uses 2; it changed the HeapTupleHeader layout again.

 * Release 8.1 uses 3; it redefined HeapTupleHeader infomask bits.

 * Release 8.3 uses 4; it changed the HeapTupleHeader layout again, and

 *      added the pd_flags field (by stealing some bits from pd_tli),

 *      as well as adding the pd_prune_xid field (which enlarges the header).

 *

 * As of Release 9.3, the checksum version must also be considered when

 * handling pages.

 */

#define PG_PAGE_LAYOUT_VERSION      4

#define PG_DATA_CHECKSUM_VERSION    1

version获取方法:

#define PageGetPageLayoutVersion(page) \

    (((PageHeader) (page))->pd_pagesize_version & 0x00FF)

Pd_prune_xid:上一次块内整理时的xid;在索引page中不使用。

Pd_linp:行指针数据,行指针的结构有三个成员组成(off,flag,len),占4字节空间;

  • 操作方法

Page空间的有效性,即指针不为空

#define PageIsValid(page) PointerIsValid(page)

Pageheader size计算:

#define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))

 

获取page内容,跳过pageheader

#define PageGetContents(page) \

    ((char *) (page) + MAXALIGN(SizeOfPageHeaderData))

 

page是否是空的,即page中一条tuple都没有,pd_lower指向pageheader结尾

#define PageIsEmpty(page) \

    (((PageHeader) (page))->pd_lower <= SizeOfPageHeaderData)

page是否是新的,还没有被pageInit初始化

#define PageIsNew(page) (((PageHeader) (page))->pd_upper == 0)

设置pagesize和version

#define PageSetPageSizeAndVersion(page, size, version) \

( \

    AssertMacro(((size) & 0xFF00) == (size)), \

    AssertMacro(((version) & 0x00FF) == (version)), \

    ((PageHeader) (page))->pd_pagesize_version = (size) | (version) \

)

3、行指针介绍

  • 结构如下:

typedef struct ItemIdData

{

    unsigned    lp_off:15,      /* offset to tuple (from start of page) */

                lp_flags:2,     /* state of line pointer, see below */

                lp_len:15;      /* byte length of tuple */

} ItemIdData;

typedef ItemIdData *ItemId;

这里为了节省空间,按bit进行了划分,lp_off/lp_len的上限也是pagesize的上限;

  • 操作方法

获取第offsetNumber行指针

#define PageGetItemId(page, offsetNumber) \

    ((ItemId) (&((PageHeader) (page))->pd_linp[(offsetNumber) - 1]))

 

获得最大行指针数量

#define PageGetMaxOffsetNumber(page) \

    (((PageHeader) (page))->pd_lower <= SizeOfPageHeaderData ? 0 : \

     ((((PageHeader) (page))->pd_lower - SizeOfPageHeaderData) \

      / sizeof(ItemIdData)))

4、空闲空间介绍

  • 空闲空间主要指pd_lower与pd_upper之间的是否有空间,能否放下tuple(tuple数据+行指针)

Size

PageGetFreeSpace(Page page)

{

    int         space;

    /*

     * Use signed arithmetic here so that we behave sensibly if pd_lower >

     * pd_upper.

     */

    space = (int) ((PageHeader) page)->pd_upper -

        (int) ((PageHeader) page)->pd_lower;

    if (space < (int) sizeof(ItemIdData))

        return 0;

    space -= sizeof(ItemIdData);

    return (Size) space;

}

  • 相关操作方法

是否有空闲行

#define PageHasFreeLinePointers(page) \

    (((PageHeader) (page))->pd_flags & PD_HAS_FREE_LINES)

 

page是否满

#define PageIsFull(page) \

    (((PageHeader) (page))->pd_flags & PD_PAGE_FULL)

 

tuple是否都可见

#define PageIsAllVisible(page) \

    (((PageHeader) (page))->pd_flags & PD_ALL_VISIBLE)

5、tuple数据介绍

    

  • 操作方法

获取itemid指定的tuple 指针

#define PageGetItem(page, itemId) \

( \

    AssertMacro(PageIsValid(page)), \

    AssertMacro(ItemIdHasStorage(itemId)), \

    (Item)(((char *)(page)) + ItemIdGetOffset(itemId)) \

)

6、special空间介绍

  • 操作方法

获取special空间大小

#define PageGetSpecialSize(page) \

    ((uint16) (PageGetPageSize(page) - ((PageHeader)(page))->pd_special))

获取special位置

#define PageGetSpecialPointer(page) \

( \

    AssertMacro(PageValidateSpecialPointer(page)), \

    (char *) ((char *) (page) + ((PageHeader) (page))->pd_special) \

)


结尾

作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。

注:未经同意,不得转载!

猜你喜欢

转载自blog.csdn.net/senllang/article/details/113952100