PostgreSQL/页面布局
PostgreSQL 将数据和关联索引存储在单独的文件中:每个表一个文件,每个索引一个文件。文件被分成 8192 字节(8 KiB)的块。这是 PostgreSQL 磁盘 I/O 的最小单位。当前使用的块在实例的共享内存中以 1:1 的比例镜像。在 PostgreSQL 中,“块”通常称为“页面”,虽然“页面”一词可能在底层文件系统中被不同地解释,例如:4 KiB 或其他一些 2^n 值。
数据文件的所有页面在逻辑上是等价的,而索引文件根据索引的需求使用不同的页面类型(元数据页面、根页面、内部树页面、叶页面,…)。然而,数据文件和所有类型索引文件的页面的物理布局是相同的。
每个页面包含 5 个主要部分
- 页面头:关于页面的通用信息。
- ItemId:指向项目的指针数组。它会随着时间的推移而增长或缩小。
- 空闲空间:页面的未用空间。
- 项目:一组实际数据,分别对应行或一组索引条目。它会随着时间的推移而增长或缩小。
- 特殊空间:数据文件不使用它。索引文件根据索引类型的需求对其进行结构化。
每增加一行或一个索引条目,就会在第 2 个部分末尾创建一个新的 ItemId,并在第 4 个部分开头创建一个新的项目。结果,空闲空间从其左右两侧缩小。
页面头包含 24 个字节,并包含诸如页面校验和、空闲空间开始和结束的偏移量或特殊空间的偏移量、WAL 处理的信息、布局版本号以及一些其他标志等信息。
每个ItemId包含 4 个字节,并包含相应项目的偏移量和长度。
在数据文件的情况下,每个项目(= 行)包含
- 项目头:23 个字节,包含各种事务 ID、当前或更新的元组 ID、数据偏移量以及各种标志。
- 空值位图:标记当前为 NULL 的列。该地图是可选的:如果表只包含非空列,那么它就是多余的。
- 数据:行的每个属性的值 - 如果不为 NULL。属性的顺序和类型存储在系统架构中。在可变长度数据类型的情况下,当前使用的长度存储在属性的开头。
在索引文件的情况下,项目包含索引条目,它们根据索引类型以不同的方式进行结构化。
特殊空间仅供索引文件用于其自身目的,例如:页号、页面类型、相同级别页面中的(双重)链接列表、树级,… 。数据文件不需要这种附加信息。它们被组织为一个堆,这意味着它们的页面在逻辑上是等价的,页面之间没有任何特殊顺序或层次结构。附加数据始终被放置到下一个空闲空间或一个新分配的页面。
通常,数据文件的某些部分通过其页号直接读取和写入。但这并不罕见,它们被完全读取。这是通过顺序扫描来完成的。这种全面的顺序访问(作为在索引文件中跟随链接列表的类比)——在大多数情况下——通过其预读技术由底层文件系统进行优化。这尤其有助于位于旋转磁盘 (HDD) 上的文件,而对于 SSD 来说,这种优势会略微消失。