PostgreSQL 12迁移后数据库大小差异过大排查求助
以下是导致新旧数据库大小差异的常见原因,结合你的场景(从PG12.7迁到PG12.14,数据行数一致)逐一说明:
旧数据库存在严重的对象膨胀
长期运行的PG实例(比如K8s上的PG12.7)会因为频繁的更新、删除操作积累大量死元组,若autovacuum配置不合理或运行不及时,表和索引会产生大量“空洞”(未被重用的存储空间),导致数据库文件实际体积远大于有效数据量。而迁移过程中(比如用pg_dump/pg_restore)会重建所有表和索引,相当于自动完成了碎片整理,新库没有这些冗余的膨胀数据,因此体积更小。
可在旧库中用pgstattuple插件查看表的膨胀率,或查询pg_stat_user_tables的n_dead_tup字段确认死元组数量。索引存储结构差异
旧库的索引随着长期的插入、删除、更新操作,会产生大量碎片化空间;而新库通过逻辑备份重建的索引是基于完整数据一次性构建的,结构更紧凑,空间利用率更高。尤其是B-tree索引,碎片化严重时体积会比紧凑状态大很多。TOAST表的冗余数据
如果数据库包含大量大字段(如text、bytea),这些数据会存储在TOAST表中。旧库的TOAST表同样会因为大字段的频繁修改积累死元组和空洞,新库迁移时TOAST数据会被重新存储,消除了冗余,进一步缩小了整体体积。表空间块填充率差异
迁移时的批量数据导入(比如COPY命令)会让PG尽可能填满每个数据块(默认块大小8KB),而旧库中因为零散的插入、更新操作,数据块的填充率可能很低,导致相同数据量占用更多存储空间。系统表与统计信息的差异
旧库长期运行过程中,系统表(如pg_statistic、pg_stat_activity)会积累大量统计数据和历史记录,而新库是全新实例,系统表仅包含基础元数据,这部分差异虽占比通常不大,但也可能贡献一部分体积差。存储层的稀疏文件特性
如果旧库所在的K8s存储卷使用了稀疏文件(比如部分云厂商的块存储默认配置),pg_database_size返回的是文件的逻辑大小,而实际物理占用可能远小于这个值;而VM上的存储可能是实际分配的物理空间,导致新库的“实际占用”与旧库的“逻辑大小”出现明显差异。可通过操作系统的du命令分别查看新旧库数据目录的物理占用,对比pg_database_size的结果验证这一点。
内容的提问来源于stack exchange,提问作者Oxycash

