表和元组的组织形式
表和索引由一个或多个表文件组成, 每个表文件被分成固定长度的page(或者叫block), block大小由代码的宏定义BLCKSZ决定, 在单机PG中,该值为8KB(8192);在ADB PG中,该值为32KB(32768). 一个表文件由固定数量的block组成, 数量由宏定义RELSEG_SIZE决定, 该值为32768. 因此,一个表文件最大的size = RELSEG_SIZE * BLCKSZ.
注: BLCKSZ的值最大智能设置为2^15=32768, 原因是用于在block内部寻址的 item pointer中, 用了15位来存一个tuple在block内部的偏移量.
pd_lsn: 表示该页面最近一次修改所对应的WAL日志的位置,该值用于数据库恢复replay WAL时,若page的pd_lsn大于此时replay WAL的lsn, 说明该页面比WAL所对应的操作更新, 无需replay pd_checksum: 页面的checksum值 pd_flags: 这些flag用于在读一个页面时,快速能判断这个页是否有 空余的line_poiner, 是否还有可用空间, 页面中的元组是否全部可见.
#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: 记录block内的偏移量,指向block free space的起始位置, 也即最后一个line pointer的结束位置 pd_upper: 记录block内的偏移量, 指向block free space的结束位置.
pd_special: block内的偏移量, 指向block最后的 special 存储区域的起始位置, 该区域用来存储索引的专用数据. 对于普通表, 该区域为空, 所以pd_special的值就等于页大小.
pd_pagesize_version: 2字节, 记录页面大小和页面版本, 前1个字节表示页面大小, 后1个字节表示版本
pd_prune_xid: 每次页面中的有元组被update/delete时,会设置这个值为当前update/delete事务ID. 之后数据库读取该页面(如调用heapgetpage) 会检查该值是否比此时的oldest min xid 还小, 如果是, 则表明该页面时可以清理的, 那么最终会调用PageRepairFragmentation对该page(block)进行清理. 清理简单讲就是将无用的元组清除(包括HOT链中的无用tuple),将有用的元组移动到页面的最后, 腾出页面中间的free space, 使得页面空间再次变得连续.
pg_linp[]: line pointer数组, 每个数组元素是是一个iitemId结构, 记录一个元组在block内的偏移量, 元组长度, 以及一些flag, 用来表示该line pointer 是否可用
typedef struct ItemIdData
{
unsigned lp_off:15, /* offset to tuple (from start of page) */
lp_flags:2, /* state of item pointer, see below */
lp_len:15; /* byte length of tuple */
} ItemIdData;