postgresql索引文件结构如何设计_postgresql索引文件格式

PostgreSQL默认使用B-tree索引,基于8KB页组织,采用多层树结构,包含根、内部和叶节点,叶子节点同层且双向链接。页由页头、元组数组、空闲空间和特殊区域组成,页头含LSN、校验和等信息,元组存储键值与TID,支持压缩。插入删除触发分裂或清理,WAL保障一致性,结构统一支持多种索引类型。

PostgreSQL 使用 B-tree 作为默认索引类型,其索引文件结构基于磁盘页(Page)组织,每个页大小通常为 8KB。整个索引结构以多层级的树形方式存储,支持高效查找、插入和删除操作。

B-tree 索引整体结构

PostgreSQL 的 B-tree 索引由多个逻辑层级组成:根节点(Root)、中间节点(Internal Nodes)和叶节点(Leaf Nodes)。数据按键值有序排列,所有叶子节点位于同一层,并通过双向链表连接,便于范围扫描。

  • 根节点是查询入口,可能直接是叶子节点(当表很小)
  • 内部节点只存键值和子页指针,用于导航
  • 叶子节点包含实际的索引项:键值 + TID(指向表中行的物理位置)

索引页(Index Page)格式

每个索引页遵循统一的底层结构,由页头、元数据、元组数组和空闲空间组成。

页头信息(PageHeaderData):
  • pd_lsn:记录最后一次修改该页的日志序列号(LSN)
  • pd_checksum:可选校验和,用于检测页损坏
  • pd_flags:标记页属性,如是否为叶子节点、根节点、删除状态等
  • pd_lower:指向页内数据起始位置(从下往上增长)
  • pd_upper:指向空闲空间起始位置
  • pd_special:指向特殊区域起始位置(B-tree 中为 IndexTupleData 结构数组)
  • pd_pagesize_version:页大小与版本号
  • pd_prune_xid:用于清理过期事务信息的最老 XID
元组数组区:
  • 从页底部向上存放 IndexTuple 结构
  • 每个 IndexTuple 包含:
    • 索引键值(Key Attributes)
    • TID(Table Page ID + Offset),指向堆表中的具体行
    • 额外标志位(如是否 NULL、是否压缩等)
    • t_info 字段记录长度和属性标志
空闲空间:
  • 位于页中部,由 pd_lower 和 pd_upper 界定
  • 新插入的索引项在此分配空间
  • 使用“反向填充”策略,提高空间利用率
特殊区域(Special Section):
  • B-tree 特有,位于页顶部,包含 BTreePageOpaqueData
  • 存储:
    • 左兄弟/右兄弟指针(用于叶子层遍历)
    • 父节点指针(非必须)
    • 层级编号(level,根为0,逐层递增)
    • 标志位(LEAF、ROOT、HALF_DEAD 等)

索引项(IndexTuple)结构细节

在 B-tree 中,IndexTuple 是基本存储单元,定义在 itup.h 中。

  • t_tid:指向表中行的 TID(BlockNumber + OffsetNumber)
  • t_info:包含长度、是否有NULL、是否有前缀压缩等标志
  • 键值部分:按索引定义顺序排列的 Datum 值,可能经过对齐或压缩
  • 支持前缀压缩(仅存与前一项不同的部分)和尾部压缩(去除重复后缀)

构建与维护机制

索引不是实时完全平衡的,而是通过写时复制(Copy-on-Write)和 WAL 日志保证一致性。

  • 插入时若页满,则触发分裂(Split):创建新页,重分布键值,更新父节点
  • 删除后标记为 DEAD,后续由 VACUUM 或 B-tree 内部清理机制回收空间
  • WAL 记录所有页变更,确保崩溃恢复后索引一致
  • 支持高级特性如部分索引、表达式索引、多列索引,其结构统一适配于上述框架
基本上就这些。PostgreSQL 的索引文件设计兼顾性能与可靠性,利用固定页结构和清晰的层次划分,实现高效的磁盘I/O和并发控制。