↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

不受支持的版本: 7.4 / 7.3 / 7.2 / 7.1 / 7.0 / 6.5 / 6.4
历史版本PostgreSQL 7.3 已于 2007 年 11 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本手册首页。

第 7 章 页文件

摘要

对数据库文件页面格式的说明。

本节概述 PostgreSQL 表和索引内部所使用的页面格式。 (索引访问方法不要求必须使用这种页面格式。 现有的所有索引方法都使用这种基本格式,但索引元页中保存的数据通常 并不完全遵循项布局规则。)TOAST 表和序列的格式与普通表相同。

在下面的说明中,假定一个 字节 (byte)包含 8 位。此外,术语 项(item) 指存储在页面上的一个单独数据值。在表中, 项是一个元组(行);在索引中,项是一个索引条目。

表 7.1 展示了页面的基本布局。 每个页面包含五个部分。

表 7.1. 页面布局示例

项 描述
PageHeaderData 长度为 20 字节。包含页面的一般信息,包括 空闲空间指针。
ItemIdData 指向实际项的(偏移量、长度)对数组。
空闲空间 未分配的空间。所有新元组都从这里分配,通常从末尾开始。
项 项本身。
特殊空间 索引访问方法特有的数据。不同方法存储不同 数据。普通表中为空。

每个页面的前 20 个字节由页头 (PageHeaderData)组成。它的格式详见 表 7.2。前两个字段处理与 WAL 有关的内容。接下来是三个 2 字节整数字段 (pd_lower, pd_upper, 和 pd_special)。它们分别表示 到未分配空间起始位置、 到未分配空间结束位置以及到 特殊空间起始位置的 字节偏移量。

表 7.2. PageHeaderData 布局

字段 类型 长度 描述
pd_lsn XLogRecPtr 8 字节 LSN:xlog 最后一个字节之后的下一个字节
pd_sui StartUpID 4 字节 最近更改的 SUI(目前只有堆 AM 使用它)
pd_lower LocationIndex 2 字节 到空闲空间起始位置的偏移量。
pd_upper LocationIndex 2 字节 到空闲空间结束位置的偏移量。
pd_special LocationIndex 2 字节 到特殊空间起始位置的偏移量。
pd_pagesize_version uint16 2 字节 页面大小和布局版本号信息。

所有细节都可以在 src/include/storage/bufpage.h 中找到。

特殊空间是页面末尾的一个区域,它在页面初始化时分配, 并包含某种访问方法特有的信息。 页头的最后 2 个字节 pd_pagesize_version 同时存储页面 大小和版本指示符。从 PostgreSQL 7.3 开始版本号为 1; 以前的版本使用版本号 0。(基本的页面布局和页头格式 没有变化,但堆元组头的布局变了。)页面大小 基本上只是作为一个交叉检验存在;一个安装中 不支持使用多于一种页面 大小。

页头之后是项标识符 (ItemIdData),每个需要四个字节。 一个项标识符包含 项起始位置的字节偏移量、其字节长度,以及若干影响解释方式的 属性位。 新的项标识符会根据需要从未分配空间的起始处分配。 当前已有多少个项标识符,可以通过查看 pd_lower 得知;分配新标识符时它会增加。 因为一个项 标识符在被释放之前永远不会移动, 所以即使项本身为了压缩空闲空间而在页面内移动, 其索引仍然可以被长期用来引用该项。 事实上,每个指向项的 指针(ItemPointer,也称 CTID)在 PostgreSQL 中都由页号和 项标识符索引构成。

项本身存储在从未分配空间末尾开始、向后分配的区域中。其确切结构取决于 表要包含什么内容。表和序列都使用一种名为 HeapTupleHeaderData 的结构体,如下所述。

最后一节是“特殊节”,它可以包含 访问方法想要存储的任何内容。普通表 完全不使用它(通过把 pd_special 设为等于页面 大小来表示)。

所有表元组的结构都相同。有一个固定 大小的头(在大多数机器上占 23 字节),后跟可选的空值 位图、可选的对象 ID 字段和用户数据。头 的细节见 表 7.3。实际的用户 数据(元组的字段)从 t_hoff 所指示的偏移处开始,它必须始终是该平台上 MAXALIGN 距离的倍数。 空值位图只有当 t_infomask 中设置了 HEAP_HASNULL 位时才存在。如果 存在,它紧跟在固定头之后,占用 足够的字节使每个数据列有一位 (即总共 t_natts 位)。在这串位中, 1 位表示非空,0 位表示空。位图 不存在时,所有列都被假定 为非空。对象 ID 只有当 t_infomask 中设置了 HEAP_HASOID 位时才 存在。如果存在,它正好出现在 t_hoff 边界之前。为使 t_hoff 成为 MAXALIGN 倍数所需的任何填充 将出现在空值位图和 对象 ID 之间。(这反过来确保对象 ID 得到适当的对齐。)

表 7.3. HeapTupleHeaderData 布局

字段 类型 长度 描述
t_xmin TransactionId 4 字节 插入 XID 戳
t_cmin CommandId 4 字节 插入 CID 戳(与 t_xmax 共用存储)
t_xmax TransactionId 4 字节 删除 XID 戳
t_cmax CommandId 4 字节 删除 CID 戳(与 t_xvac 共用存储)
t_xvac TransactionId 4 字节 移动元组的 VACUUM 操作的 XID
t_ctid ItemPointerData 6 字节 此元组或更新元组的当前 TID
t_natts int16 2 字节 属性数量
t_infomask uint16 2 字节 各种标志位
t_hoff uint8 1 字节 到用户数据的偏移量

所有细节都可以在 src/include/access/htup.h 中找到。

要解释实际数据,必须借助从其他表中获得的信息,其中大部分来自 pg_attribute。 所需的特定字段是 attlen 和 attalign。除非所有字段都是定宽且没有 空值,否则没有办法直接取得某个特定属性。 所有这些技巧都封装在 heap_getattr、fastgetattr 和 heap_getsysattr 这些函数中。

读取数据时,需要依次检查每个属性。首先根据空值位图判断该字段是否为 NULL。如果是,就继续下一个。然后确认对齐是否正确。如果字段是 定宽字段,那么它的所有字节都是直接摆放的;如果它是 变长字段(attlen == -1),情况就会稍复杂一些, 需要使用变长结构 varattrib。 根据标志位的不同,数据可能是线内的、经过压缩的,或者位于 另一个表中(TOAST)。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。