这个现象很典型 :单个表看上去都不大,但整个库(database)占用空间却很大。在 PostgreSQL 里通常有几类可能原因,可以一步步排查。


1. 库大小包含的内容

数据库大小 =

  • 表数据 + 索引
  • TOAST 表(大字段存储)
  • 系统表
  • 膨胀空间(dead tuples,还没 vacuum full 回收)
  • 其他对象(临时表、未清理的表)

所以只查 pg_statio_user_tables 的表大小,可能漏掉不少东西。


2. 排查步骤

✅ 步骤 1:确认数据库大小

SELECT pg_size_pretty(pg_database_size(current_database()));

✅ 步骤 2:查看库里所有对象(表+索引+toast)

SELECT 
    nspname AS schema_name,
    relname AS object_name,
    relkind,
    pg_size_pretty(pg_total_relation_size(pg_class.oid)) AS total_size
FROM pg_class
JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
WHERE relkind IN ('r','i','t')   -- r=表, i=索引, t=TOAST表
ORDER BY pg_total_relation_size(pg_class.oid) DESC;

这样能看到:

  • 哪些索引很大
  • 哪些 TOAST 表占了很多空间

✅ 步骤 3:检查是否有膨胀 (bloat)

表和索引可能被更新/删除过很多次,导致文件很大,但有效数据很少。

SELECT 
    schemaname, relname,
    n_live_tup, n_dead_tup
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;
  • n_dead_tup 很大说明有很多“死元组”,需要 VACUUM FULL 或重建索引。

✅ 步骤 4:检查临时对象

有时候应用产生了临时表/临时索引没清理。

SELECT nspname, relname
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE nspname LIKE 'pg_temp%';

✅ 步骤 5:确认是不是 WAL / 系统目录占用

数据库大小(pg_database_size)只算 base/DBOID/,不包括 pg_wal
所以如果 du -sh $PGDATA 看到很大,但 pg_database_size() 很小,多半是 WAL 没清理(归档/复制没跑通)。


3. 解决思路

  • 如果是 索引太大REINDEX TABLE xxx;
  • 如果是 TOAST 占用 → 检查大字段,是否需要清理或分离存储
  • 如果是 膨胀VACUUM FULLCLUSTER 重写表
  • 如果是 临时表 → 清理掉
  • 如果是 WAL → 检查归档/复制是否正常

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐