PageHeaderData:Page 的页面头
在 PostgreSQL 中,可以把一个 B-Tree Page 近似理解为一个 B-Tree 节点。
PageHeaderData 并不是整个节点,而是 Page 最前面的页面头,用于保存 PostgreSQL 管理这个 Page 所需要的基本信息。
一个 B-Tree Page 的整体布局可以简单理解为:
python
低地址 / Page 开头
↓
┌────────────────────────────┐
│ PageHeaderData │
│ pd_lsn │ ← WAL 日志位置
│ pd_checksum │ ← 页面校验和
│ pd_flags │ ← 页面状态
│ pd_lower │
│ pd_upper │
│ pd_special │
│ pd_pagesize_version │ ← 页面大小、版本
│ pd_prune_xid │ ← 清理相关事务 ID
├────────────────────────────┤
│ pd_linp[] │ ← Line Pointer 数组
├────────────────────────────┤ ← pd_lower
│ │
│ Free Space │
│ 空闲空间 │
│ │
├────────────────────────────┤ ← pd_upper
│ Tuple / IndexTuple │
│ Tuple / IndexTuple │
│ Tuple / IndexTuple │
├────────────────────────────┤ ← pd_special
│ Special Space │
│ B-Tree: BTPageOpaqueData │
└────────────────────────────┘
↑
高地址 / Page 结尾
各字段的作用:
pd_lsn:Log Sequence Number,记录这个 Page 最近一次修改对应的 WAL 日志位置,主要用于 WAL 和崩溃恢复。pd_checksum:页面校验和,用于帮助检测 Page 数据是否损坏。pd_flags:Page 的通用状态标志。注意它和 B-Tree 专用的btpo_flags不是一回事。pd_lower:空闲空间的起始位置。也就是 Line Pointer 数组结束的位置。pd_upper:空闲空间的结束位置,也是 Tuple 数据区域的起始位置。pd_special:Special Space 的起始位置。对于 B-Tree Page,这块区域中存放BTPageOpaqueData。pd_pagesize_version:保存 Page 大小以及 Page 布局版本相关信息。pd_prune_xid:记录与 Page 数据清理(prune)相关的事务 ID。pd_linp[]:Line Pointer 数组。每个 Line Pointer 用于定位 Page 中对应的 Tuple / IndexTuple。
其中最值得关注的是:
sql
pd_lower
↓
┌────────────────────────────┐
│ Free Space │
└────────────────────────────┘
↑
pd_upper
因此,pd_lower 和 pd_upper 共同描述了 Page 当前的空闲空间范围,而 pd_special 则标记了 Page 尾部特殊区域的开始位置。
简单来说:
PageHeaderData就是一个 Page 的"管理信息表",记录日志位置、校验信息、页面状态、空闲空间位置以及 Tuple 定位信息等。
附实际代码定义
go
typedef struct PageHeaderData
{
/*
* XXX:LSN 是任何 block 都具有的成员,
* 并不只是采用 page 组织形式的 block 才有
*/
PageXLogRecPtr pd_lsn; /* LSN:最后一次修改此页面的 WAL/XLOG
* 记录结束位置之后的下一个字节位置 */
uint16 pd_checksum; /* 页面校验和 */
uint16 pd_flags; /* 页面标志位,具体标志见下面的定义 */
LocationIndex pd_lower; /* 空闲空间起始位置的偏移量 */
LocationIndex pd_upper; /* 空闲空间结束位置的偏移量 */
LocationIndex pd_special; /* 特殊空间(special space)起始位置的偏移量 */
uint16 pd_pagesize_version; /* 页面大小和页面布局版本信息 */
TransactionId pd_prune_xid; /* 页面中最老的、可能被清理(prune)的事务 ID;
* 如果没有,则为 0 */
ItemIdData pd_linp[FLEXIBLE_ARRAY_MEMBER];
/* 行指针(line pointer)数组 */
} PageHeaderData;