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


21

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

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

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

Answers:


29

要将空间返回给操作系统,请使用VACUUM FULL。我想你正在奔跑VACUUM FULL ANALYZE我引用该手册

FULL

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

大胆强调我的。

CLUSTER 作为附带效果也可以达到目的。

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

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;

现在,最后一页为空。这将忽略并发写入。您要么是对该表的唯一写操作,要么需要采取写锁操作以避免干扰。

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

SQL Fiddle(重用不同情况下的简单表。)

测量行/表大小的工具:

磁盘已满

您需要在磁盘上摆动空间才能进行任何这些操作。还有一个社区工具pg_repack可以代替VACUUM FULL/ CLUSTER。它避免了排他锁,但也需要可用空间来使用。手册:

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

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

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

将表转储到具有足够磁盘空间(用于)的远程计算机所连接的磁盘上-a--data-only

从远程外壳中,转储表数据:

pg_dump -h <host_name> -p <port> -t mytbl -a mydb > db_mytbl.sql

在pg会话中,TRUNCATE该表:

-- drop all indexes and constraints here for best performance
TRUNCATE mytbl;

从远程shell恢复到同一表:

psql -h <host_name> -p <port> mydb -f db_mytbl.sql
-- recreate all indexes and constraints here

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

但是也许您可以更简单一些?

  • 您可以通过删除(移动)不相关的文件来在磁盘上腾出足够的空间吗?

  • 您可以VACUUM FULL先逐个缩小表,以释放足够的磁盘空间吗?

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

无论您做什么,都不要着急。如有疑问,请先将所有内容备份到安全位置。


欧文(Erwin),对不起,我忘了提到我没有足够的空间来容纳真空。更新了问题。
左右一切

@Zapadlo:我为更新的问题添加了一个章节。
Erwin Brandstetter

感谢您的全面答复。实际上,我认为我可以通过虚假更新将死行放在db页的末尾,即update table set field_1 = field_1,但是在该操作未能返回可用空间后清理该表,有什么想法吗?
大约

@Zapadlo:我的想法已经在答案中了。:)我不知道可以在磁盘上不需要大量摆动空间的情况下重新排序死元组的工具。(这并不意味着那里没有人。)
Erwin Brandstetter

他们说,这个工具做的伎俩,虽然还没有尝试过:code.google.com/p/pgtoolkit/source/browse/trunk/bin/...
错误-约-一切
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.