VACUUM 将磁盘空间返还给操作系统

Vad*_*hin 30 postgresql maintenance disk-space vacuum postgresql-9.2

VACUUM通常不会将磁盘空间返回给操作系统,除非在某些特殊情况下。
从文档:

VACUUM删除表和索引中的死行版本并标记可用空间以供将来重用的标准形式。但是,它不会将空间返回给操作系统,除非在表末尾的一个或多个页面完全空闲并且可以轻松获得排他表锁的特殊情况下。相比之下,VACUUM FULL通过编写一个没有死空间的完整新版本的表文件来主动压缩表。这最大限度地减少了表的大小,但可能需要很长时间。它还需要额外的磁盘空间用于表的新副本,直到操作完成。

问题是:如何实现这个数据库状态one or more pages at the end of a table become entirely free?这可以通过 完成VACUUM FULL,但我没有足够的空间来实现它。那么还有没有其他可能呢?

Erw*_*ter 45

要将空间返回给操作系统,请使用VACUUM FULL. 在此期间,我想您会跑VACUUM FULL ANALYZE. 我引用手册

FULL

选择“完全”真空,这可以回收更多空间,但需要更长的时间并且独占锁定表。此方法还需要额外的磁盘空间,因为它会写入表的新副本,并且在操作完成之前不会释放旧副本。通常这应该只在需要从表中回收大量空间时使用。

大胆强调我的。

CLUSTER 也可以作为附带效果实现这一点。

PlainVACUUM通常无法实现您的目标(“表格末尾的一页或多页完全免费”)。它不会对行重新排序,而只会在机会出现时从文件的物理末尾修剪空页 - 就像您从手册中引用的那样。

在附加其他元组之前INSERT,当您处理一批行和DELETE它们时,您可以在物理文件的末尾获得空页。或者,如果删除了足够多的行,这可能是巧合。

还有一些特殊设置可能会阻止VACUUM FULL回收空间。看:

在表的末尾准备空页以进行测试

系统列ctid代表一行的物理位置。您需要了解该列:

我们可以使用它并通过删除最后一页中的所有行来准备一个表格:

DELETE FROM tbl t
USING (
   SELECT (split_part(ctid::text, ',', 1) || ',0)')::tid     AS min_tid
        , (split_part(ctid::text, ',', 1) || ',65535)')::tid AS max_tid
   FROM   tbl
   ORDER  BY ctid DESC
   LIMIT  1
   ) d
WHERE  t.ctid BETWEEN d.min_tid AND d.max_tid;
Run Code Online (Sandbox Code Playgroud)

现在,最后一页是空的。这会忽略并发写入。要么你是唯一一个写入该表的人,要么你需要采取写锁来防止竞争条件。

该查询经过优化,可以快速识别符合条件的行。a 的第二个数字tid是存储为 unsigned 的元组索引int2,并且65535是该类型 ( 2^16 - 1)的最大值,因此这是安全上限。

db<>fiddle here (重用来自不同案例的简单表。)
旧的sqlfiddle

测量行/表大小的工具:

磁盘已满

对于任何这些操作,您都需要在磁盘上有回旋余地。还有社区工具pg_repack作为VACUUM FULL/ 的替代品CLUSTER。它避免了排他锁,但也需要可用空间。手册:

需要两倍于目标表和索引的可用磁盘空间。

作为最后的手段,您可以运行转储/恢复周期。这也消除了表和索引中的所有膨胀。密切相关的问题:

那边的答案非常激进。如果您的情况允许(没有外键或其他引用阻止行删除),并且没有对表的并发访问),您可以:

将表转储到从具有足够磁盘空间远程计算机连接的磁盘-afor --data-only):

从远程 shell 转储表数据:

pg_dump -h <host_name> -p <port> -t mytbl -a mydb > db_mytbl.sql
Run Code Online (Sandbox Code Playgroud)

在 pg 会话中,TRUNCATE该表:

-- drop all indexes and constraints here for best performance
TRUNCATE mytbl;
Run Code Online (Sandbox Code Playgroud)

从远程 shell,恢复到同一个表:

psql -h <host_name> -p <port> mydb -f db_mytbl.sql
-- recreate all indexes and constraints here
Run Code Online (Sandbox Code Playgroud)

它现在没有任何死行或膨胀。

但也许你可以更简单?

  • 你能通过删除(移动)不相关的文件来腾出足够的磁盘空间吗?

  • 能否VACUUM FULL先将表一个一个地变小,从而释放足够的磁盘空间?

  • 您可以运行REINDEX TABLEREINDEX INDEX从膨胀的索引中释放磁盘空间吗?

无论你做什么,都不要鲁莽。如果有疑问,请先将所有内容备份到安全位置。