如何使用pageinspect查看PostgreSQL表的空闲空间?

来源:菜鸟站长作者:宋承宪头衔:网络博主
导读:本期聚焦于小伙伴创作的《如何使用pageinspect查看PostgreSQL表的空闲空间?》,敬请观看详情。表膨胀和空间回收是PostgreSQL运维里的核心难题。很多人以为执行VACUUM就能掌握真实空闲情况,其实只有借助pageinspect这类底层扩展,才能看清每个数据页的空闲字节数。该扩展提供heap_page_items、page_header等函数,可直接读取表文件的页面结构。通过解析pd_upper与pd_lower的差值,能算出页内未使用空间,再逐页统计即可得到全局空闲量。相比单纯依赖pgstattuple估算,pageinspect给出的是精确的物理页面视图,对定位膨胀热点、判断是否需要VACUUM FULL很有价值。

在PostgreSQL中,表的数据以固定大小的页面(默认8KB)存储,页面内部存在已用和未用区域。当表经历大量更新和删除后,页面中会散落许多 dead tuple,形成空闲空间。要准确掌握这些空闲空间的分布,可以借助contrib模块pageinspect直接解析底层页面结构。

如何使用pageinspect查看PostgreSQL表的空闲空间?

pageinspect扩展的准备工作

pageinspect是PostgreSQL自带的一个贡献模块,提供了若干函数用于检查数据库页面的内容。在使用之前,需要以超级用户身份在目标数据库中创建该扩展。如果数据库未安装contrib包,则需先通过操作系统包管理器安装,例如在某些Linux发行版中对应postgresql-contrib软件包。

创建扩展的语句非常简单,但需要注意权限问题。只有具备超级用户权限或者拥有该数据库 CREATE 权限的角色才能执行。扩展创建后,相关的函数如表page_header、heap_page_items就会注册到当前数据库的public模式下,后续查询可以直接调用。

-- 以超级用户连接目标库后执行
CREATE EXTENSION IF NOT EXISTS pageinspect;

-- 确认函数已存在
df page_header
df heap_page_items

理解页面头与空闲空间的计算原理

每一个PostgreSQL堆页面都有一个页面头结构,其中两个关键字段是pd_lower和pd_upper。pd_lower指向行指针(item pointer)数组的末尾,而pd_upper指向实际元组数据的起始位置。页面中的空闲空间大小,就是pd_upper减去pd_lower所得的差值。当表中删除大量记录但未做VACUUM时,pd_upper不会立即回缩,空闲空间便停留在页面中间。

通过pageinspect提供的page_header函数,可以读取指定页面的头信息。该函数接收一个页面二进制数据作为参数,通常配合get_raw_page函数从表中抽取原始页面。理解这两个字段的关系,是手动计算空闲空间的基础,也比单纯看统计视图更能反映物理存储的真实状态。

-- 读取某表第0号页面的头部信息
SELECT *
FROM page_header(get_raw_page('test_table', 0));

-- 输出示例中的 pd_lower 与 pd_upper 字段
-- pd_lower | pd_upper
-- 232      | 7920
-- 空闲字节 = 7920 - 232 = 7688

逐页统计表的空闲空间

单页查看无法反映整张表的情况,实际运维中往往需要遍历所有页面并求和。PostgreSQL的表由多个物理文件分支组成,每个分支又由从0开始的页面号排列。我们可以借助generate_series生成页面号序列,并结合page_header批量计算。

下面的示例通过关联get_raw_page和page_header,对指定表的前N个页面进行扫描。需要注意的是,若页面号超出实际范围,get_raw_page会报错,因此通常要先从pg_class获取relpages估算总量,或利用异常处理避免中断。统计结果中的空闲总和,能帮助判断表内碎片程度。

-- 假设表名是 test_table,扫描前100个页面
SELECT
  blk AS page_num,
  (page_header(get_raw_page('test_table', blk))).pd_upper AS upper,
  (page_header(get_raw_page('test_table', blk))).pd_lower AS lower,
  (page_header(get_raw_page('test_table', blk))).pd_upper -
  (page_header(get_raw_page('test_table', blk))).pd_lower AS free_bytes
FROM generate_series(0, 99) AS blk;

-- 汇总全部空闲空间
SELECT sum(
  (page_header(get_raw_page('test_table', blk))).pd_upper -
  (page_header(get_raw_page('test_table', blk))).pd_lower
) AS total_free
FROM generate_series(0, 99) AS blk;

结合heap_page_items分析碎片类型

page_header只能看到空闲总量,若想区分这些空间是连续的还是被dead tuple割裂的,需要用heap_page_items查看行项。该函数返回页面中每一行的ctid、lp_len、t_xmin、t_xmax等字段。通过t_xmax不为0且未被冻结,可判断出哪些是已删除但未清理的死元组。

当heap_page_items显示大量短小死元组穿插在活元组之间,即便pd_upper与pd_lower差值较大,实际可复用空间也因碎片严重而有限。此时单纯依赖VACUUM可能不够,需要考虑VACUUM FULL或者pg_repack来重整页面。pageinspect因此成为决策前的重要诊断手段。

-- 查看0号页面中所有行项
SELECT lp, lp_off, lp_len, t_xmin, t_xmax, t_data
FROM heap_page_items(get_raw_page('test_table', 0));

-- 筛选死元组(t_xmax 非零表示已被删除或更新)
SELECT count(*) AS dead_rows
FROM heap_page_items(get_raw_page('test_table', 0))
WHERE t_xmax <> 0;

与pgstattuple的对比及使用建议

除了pageinspect,PostgreSQL还提供pgstattuple扩展来估算表级空闲空间。pgstattuple扫描全表并给出大致的dead_percent和free_percent,使用更方便,但它是基于采样或轻量扫描的估算值。pageinspect则是逐页精确解析,适合在怀疑统计信息失真或需要定位具体膨胀页面时使用。

在生产环境中,不建议频繁对大表全量使用pageinspect,因为get_raw_page会读取共享缓冲区或磁盘,且占用会话资源。推荐先通过pgstattuple做全局判断,再针对可疑表用pageinspect抽样少数页面验证。这样既能控制开销,又能获得可靠的空闲空间视图。

工具精度性能开销适用场景
pgstattuple估算快速查看表级膨胀率
pageinspect精确定位具体页面碎片与空闲

常见误区与注意事项

一个常见误区是认为pd_upper和pd_lower的差就是“可回收空间”。实际上,若页面中存在未清理的dead tuple,它们占据的空间并不包含在差值里,只有执行VACUUM后,行指针收缩、pd_upper回退,差值才代表真正可插入新行的区域。因此看空闲空间要结合事务状态和清理情况。

另外,pageinspect读取的是当前时刻的页面快照,若数据库正在写入,结果可能随查询过程变化。对关键业务表操作时,应尽量在低峰期进行,并避免长事务持有页面引用。掌握这些细节,才能让pageinspect真正成为查看空闲空间的好帮手。

pageinspectPostgreSQL空闲空间修改时间:2026-08-11 01:15:38

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。