PostgreSQL集群A、B表行数一致但大小差异大的原因排查
问题分析:PostgreSQL 11→14同步后行数一致但表体积差异悬殊
这是个很常见但容易让人困惑的场景,行数完全一致但表大小差了5倍多,核心原因基本可以锁定在PostgreSQL版本存储优化和DMS写入模式带来的无碎片数据这两点上,我来给你拆解具体细节:
1. 集群A存在大量数据碎片(死元组)
你的集群A是PostgreSQL 11,且应该是长期运行的生产集群,表public.x大概率经历过频繁的更新、删除操作。PostgreSQL的MVCC机制会保留旧版本的元组(也就是死元组),如果VACUUM没有及时彻底清理这些死元组,它们会一直占用磁盘空间,导致表体积膨胀。
而DMS是把集群A的全量数据批量插入到集群B中,相当于写入了一份“干净”的、没有历史版本的数据,集群B的表没有任何更新删除留下的碎片,存储密度自然高很多。
你可以在集群A执行以下命令验证死元组的情况:
SELECT relname, n_live_tup AS 存活行数, n_dead_tup AS 死元组数量, pg_size_pretty(pg_relation_size(relid)) AS 表实际数据大小, pg_size_pretty(pg_total_relation_size(relid)) AS 含索引总大小 FROM pg_stat_user_tables WHERE relname = 'x';
对比集群B的同命令结果,你会发现A的死元组数量可能非常可观,这就是体积膨胀的主要原因之一。
2. PostgreSQL 14的存储优化特性
从PostgreSQL 11到14,官方做了不少存储相关的优化,直接让相同数据的存储体积变小:
- TOAST压缩默认优化:PostgreSQL 12之后默认对大字段(text、bytea等)的TOAST存储启用了更高效的压缩算法(默认从
pglz改为lz4,压缩比更高且性能更好),如果你的表x包含大量大字段,这个优化会显著减少存储空间。 - 元组存储更紧凑:PostgreSQL 14对heap元组的头部信息做了精简,减少了每条记录的额外开销,积少成多后也会带来明显的体积差异。
- 默认启用更多存储优化参数:比如
vacuum_cleanup_index_scale_factor等参数的调整,让新集群的数据更不容易产生碎片,保持紧凑存储。
你可以通过以下命令检查两张表的TOAST配置:
SELECT reloptions FROM pg_class WHERE relname = 'x' AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public');
如果集群B的结果包含toast_compress=lz4,而集群A没有,那这就是体积差异的另一个关键因素。
3. 额外验证建议
如果上面的检查还不够明确,你可以尝试:
- 在集群A执行
VACUUM FULL public.x(注意锁表风险,建议在维护窗口执行),之后再看\dt+的结果,体积应该会明显缩小,接近集群B的大小。 - 对比两张表的索引大小:
\di+ public.x*,如果集群A的索引因为碎片也膨胀了,那总大小差异会更明显,但你提到的是表大小差异,所以主要还是heap部分的问题。
内容的提问来源于stack exchange,提问作者pkruk
相关产品推荐
相关产品推荐

