Postgres row_number() 大约每 24 小时将 table 大小加倍

Postgres row_number() doubling table size roughly every 24 hours

我有一个资产 table,里面有大约 165,000 行。但是,资产构成“集合”,每个集合可能有约 10,000 个项目,我想为其保存一个“排名”,以便用户可以看到给定资产在集合中的排名。

排名可能会发生变化(基于内部分数),因此需要定期更新(每小时几次)。

目前正在对每个集合进行此操作:

UPDATE assets a
SET rank = a2.seqnum
FROM
  (SELECT a2.*,
          row_number() OVER (
                             ORDER BY elo_rating DESC) AS seqnum
   FROM assets a2
   WHERE a2.collection_id = #{collection_id} ) a2
WHERE a2.id = a.id;

但是,这导致 table 的大小大约每 24 小时翻一番(即 1GB 到 2GB)。

A VACUUM FULL 解决了这个问题,但这并不是真正的解决方案。

能否调整查询以不创建太多(我假设是)临时存储?

运行 PostgreSQL 13.

每次更新都会在 Postgres 中写入一个新的行版本。因此(除了 TOASTed 列之外)更新 table 中的每一行大致使其大小翻倍。这就是你所观察到的。稍后可以清理死元组以缩小 table 的物理大小——这就是 VACUUM FULL 所做的, 非常昂贵 。参见:

或者,您可以 而不是 运行 VACUUM FULL 并将 table 保持在最小物理尺寸的两倍左右。如果你 运行 plain VACUUM (没有 FULL!)足够了——如果你没有长时间的 运行ning 事务阻止它——Postgres 将在free-space 映射到下一个 UPDATE 开始时可以重新使用磁盘 space,因此 保持 为最小大小的两倍。这可能比一直缩小和重新增长 table 更便宜,因为最昂贵的部分通常是物理增长 table。请务必为 table 设置激进的 autovacuum 设置。参见:

可能更好 但是,将排名分解为最小的单独 1:1 table (a.k.a."垂直分区"),因此只需“每小时几次”只写最少的行。可能包括您在查询中提到的 elo_rating,它似乎至少经常变化(?)。 (LEFT) JOIN 到查询中的主要 table。虽然这增加了相当大的开销,但它可能仍然(实质上)更便宜。取决于完整的图片,最重要的是 table assets 中的平均行大小和除了昂贵的更新之外的典型负载。

参见: