Postgres是否会在更新时重写整行?

时间:2014-03-28 01:47:35

标签: sql windows postgresql diskspace

我们在Windows 2008 Server上运行Postgres 9.0。有一个大表包含bytea列,用于存储每行0-5MB的二进制数据:

CREATE TABLE files
(
  file_id serial NOT NULL,
  data bytea NOT NULL,
  on_disk boolean,
  CONSTRAINT files_pkey PRIMARY KEY (file_id)
)

最近我们一直在更新每行的on_disk字段(不接触数据字段)。我们认为这会占用我们的临时表空间(或其他东西)中的空间,原因有两个:

1)我们开始在运行大型查询的系统的其他随机部分中收到此错误:

ERROR: 53100: could not write block 92271 of temporary file

2)我们的免费空间在一周内从大约7GB降至1.5GB,这是不寻常的。

任何人都可以确认:

a)更新postgres中的行会导致它重写ENTIRE行(包括大型二进制数据)而不释放旧空间吗?这可以解释我们的症状

b)是否在更改期间写入其他临时表空间也占用了空间? (我们可以强制释放临时空间吗?)

c)有没有办法我们可以对这个表执行次要的布尔字段更新而不是每次都重写行(并且咀嚼磁盘空间)?

d)我们可以定期强制postgres释放已用空间而不重写整个表吗? (我们已知的释放空间的方法涉及一个表格重写,我们没有空间)

P.S。:是的我们正在将我们的服务器迁移到具有更大存储空间的主机......这可能需要1-2个月的时间。

2 个答案:

答案 0 :(得分:3)

从您的问题中挑选 c)

  

有没有办法可以对此执行次要的布尔字段更新   table每次都没有重写行(并且正在咀嚼磁盘空间)?

作为@Craig already explained,“TOAST-able”且大于某个阈值的列在每个表的专用TOAST表中存储在线外(单独的“关系分支”,磁盘上的单独文件) 。因此,如果列本身未更改,则5 MB bytea列将在更新中保持不变。 The manual:

  

在UPDATE操作期间,未更改字段的值通常是   保持原样;所以带有行外值的行的UPDATE会产生   如果没有任何外线值改变,则没有TOAST成本

大胆强调我的 主关系fork中的行仍然被复制,并且更新时死行仍然落后(无论是否实际更改了任何值)。对于大行大小,以下解决方案可能需要付费:

为频繁更改的标记创建一个小的单独的1:1表。只是主键(=同时是外键)和频繁更改的标志。这样可以更快地更新并保留磁盘空间 - 初始额外开销和需要加入两个表的查询的一些成本(其他查询实际上变得更快)。有关表行的磁盘空间要求的更多信息:

答案 1 :(得分:2)

至少在9.3版本中,PostgreSQL不会重写TOAST表格中存储的字段(如果它们存储在行外)。我不知道这在9.0中是否属实。

您可以查看\d+ tablename列使用的存储空间; storage列显示使用的模式。如果单个元组足够小(例如:<2K),则可以在线压缩存储,即使在元组有资格进行行外存储的extended存储列中也是如此。

请参阅the documentation for TOASTALTER TABLE ... SET STORAGE

临时文件存储在temp_tablespaces中。默认情况下,这是空的,在这种情况下,它会回退到default_tablespace,如果为空,则会回退到pg_default表空间。

表/索引中的空间应该被释放,以便autovacuum自动重用。确保您的autovacuum守护程序运行得足够频繁并且没有设置太多的cost_delay。自9.0以来,Autovacuum得到了显着改善。

如果您想将空间释放回操作系统或用于其他表格,您需要VACUUM FULLuse an external tool like pg_repack以较少侵入性的方式进行操作。