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

Bre*_*ill 7 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)
)
Run Code Online (Sandbox Code Playgroud)

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

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

ERROR: 53100: could not write block 92271 of temporary file
Run Code Online (Sandbox Code Playgroud)

2) 我们的可用空间在一周内从 ~7GB 下降到 1.5GB,这是不寻常的。

任何人都可以确认:

a) 在 postgres 中更新一行是否会导致它在不释放旧空间的情况下重写整个行(包括大型二进制数据)?这将解释我们的症状

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

c) 有没有一种方法可以对这个表执行次要的布尔字段更新,而无需每次都重写行(&咀嚼磁盘空间)?

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

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

Erw*_*ter 6

从您的问题中选择c)

有没有一种方法可以对这个表执行次要的布尔字段更新,而无需每次都重写行(&chewing up diskspace)?

正如@Craig 已经解释过的那样,“可 TOAST”且大于某个阈值的列被外联存储在每个表的专用 TOAST 表中(单独的“关系叉”,磁盘上的单独文件)。因此,bytea如果列本身未更改,则 5 MB列在更新中将保持不变。手册:

在 UPDATE 操作期间,未更改字段的值通常按原样保留;因此,如果没有任何外线值更改,则具有外线值的行的 UPDATE 不会产生 TOAST 成本。

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

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


Cra*_*ger 5

至少在 9.3 上,TOAST如果表中的字段存储在行外,则 PostgreSQL 不会重写这些字段。不知道9.0是不是这样。

您可以使用以下命令查看列使用了哪些存储\d+ tablename;该storage列显示所使用的模式。如果单个元组足够小(例如:< 2K),则可以在线压缩存储,即使在元组extended符合行外存储条件的存储列中也是如此。

请参阅和 的文档TOASTALTER TABLE ... SET STORAGE

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

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

如果您想将空间释放回操作系统或用于其他表,则需要VACUUM FULL使用外部工具,例如pg_repack以侵入性较小的方式执行此操作。