PostgreSQL堆表顺序扫描:页与文件跳转机制技术问询
PostgreSQL堆表页关联与顺序扫描机制解析
堆表页的关联方式:并非链表结构
PostgreSQL堆表的页之间没有在页内部存储链表式关联指针,而是依赖物理存储的连续块号实现页的顺序关联:
- 堆表数据文件以表OID命名,页从0开始按顺序编号,页(i)的下一页直接是页(i+1),完全通过块号递增逻辑关联,无需额外指针存储。
- 你观察到的「页目录(ItemIdData数组)」仅用于定位页内元组,记录元组在页内的偏移量与状态,和页之间的关联无关。
顺序扫描的核心逻辑
1. 缓冲管理器获取下一页的方式
顺序扫描的扫描器(HeapScanDesc结构体)会维护当前扫描的块号(rs_cblock),获取下一页时:
- 直接计算目标块号:当前块号 + 1;
- 调用缓冲管理器的
ReadBuffer接口,传入表的关系对象(Relation)和目标块号; - 缓冲管理器先检查共享缓冲池是否已缓存该块,存在则直接返回缓冲块;不存在则从磁盘对应文件位置读取该页到缓冲池。
2. 触发跳转至下一页的条件
当扫描器处理完当前页的所有有效元组时,自动跳转至下一页:
- 扫描器遍历当前页的
ItemIdData数组,逐个处理标记为LP_USED(有效)的元组; - 遍历完所有有效元组后,确认当前页无更多待处理数据,便递增块号,切换到下一页继续扫描。
3. 触发跳转至下一个文件的条件
PostgreSQL默认单表文件大小上限为1GB,超出后会生成分段文件(如RELOID.1、RELOID.2),每个分段文件最多包含1GB/8KB=131072个页:
- 扫描器维护的是全局块号,当全局块号达到当前分段文件的最大块数(如第一个文件的块号范围为0~131071)时;
- 缓冲管理器会通过全局块号计算对应的分段文件编号(块号 / 131072)和文件内局部块号(块号 % 131072),自动切换到目标分段文件读取对应页。
源码层面的关键实现参考
在heapam.c的heap_getnext方法中,实际通过heap_scan_getnext推进扫描流程:
HeapScanDesc中的rs_cblock记录当前扫描块号,rs_nblocks记录表的总块数;- 处理完当前块后,会检查
rs_cblock是否小于rs_nblocks,若成立则递增块号并读取下一页; - 缓冲管理器的
ReadBuffer内部通过RelationGetBlockNumber、smgrnblocks等函数计算文件位置,完成跨文件读取逻辑。
内容的提问来源于stack exchange,提问作者scameeer
相关产品推荐
相关产品推荐

