UPDATE 是否会为未更改的 TOAST 值写入新的行版本?

Jas*_*son 6 postgresql disk-space vacuum update autovacuum

我正在使用一个带有大型 TEXT 字段的 PostgreSQL 表,理论上该表会定期更新。我曾考虑过将数据直接存储在文件系统中,但使用 TOAST 时,数据已经存储在页外并压缩在数据库中,所以我想我会让事情变得简单,只使用数据库存储。

为了提供一些背景信息,我正在为 RSS 提要建立索引。我将每 24 小时运行一个脚本来提取 RSS 源并可能更新表格。这可能会导致大量死元组,从而占用大量磁盘空间。当然,autovacuum 最终会处理这个问题,但它可能会产生大量数据(很多 GB),我想确保我知道当我在这个非常大的表上进行大量更新时会发生什么。

我的一个解决方案是仅在提要发生某些重大更改(例如网站上出现新帖子)时才更新 TEXT 字段(存储 RSS 数据)。这意味着我可以避免进行更新,除非确实必须这样做。但是,我仍然想更新该表(以跟踪我最近执行 HTTP 请求的时间)。这将使用旧版本的行数据创建一个死元组。

如果 TEXT 数据实际上没有改变,会发生什么情况?当 UPDATE 创建死元组时,它还会复制 TEXT 数据吗?或者 TEXT 数据会保持不变,因为它没有更改并且存储在页外?

Erw*_*ter 7

这是 Postgres MVCC 模型中采用的一个主要捷径(与+UPDATE相比):存储在外线(TOASTed)且未更改的字段保持原样。这意味着,主关系中的旧(即将失效)行版本和新行版本指向相同的 TOAST 值,没有额外的膨胀DELETEINSERTUPDATE

或者正如手册所说

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

正如 a_horse_with_no_name 指出的那样:
“未在UPDATE”中更改,或手册中所说的“未更改”,意味着“未在”SET的子句中定位UPDATE。Postgres 不会验证新列值是否确实与先前的行版本不同。

如果可能,通过添加子句来跳过一开始没有更改的行WHERE。这仍然会导致同时更新多个列的情况,其中一些列保持不变。如果这适用于您的大专栏,则可能需要单独更新它,并且仅在实际发生变化的地方进行更新。看:

OTOH,如果您定期更新大型 TOAST 字段,请考虑对这些列使用LZ4 压缩算法(Postgres 14 中的新功能)。磁盘占用空间稍大,但性能要好得多。看: