PostgreSQL 18 预览: 引入 autovacuum_vacuum_max_threshold

John Doe 三月 31, 2025

你有没有遇到过,数据库中不同大小的表,垃圾清理的效率差异很大的情况?现在,PostgreSQL 有了一个新的 autovacuum_vacuum_max_threshold 参数。

沙滩上漫步的大象

特性提交日志

引入 autovacuum_vacuum_max_threshold 参数。

自动清理(autovacuum)在选择要进行清理的表时,采用的一种方式是,将已更新或已删除的元组数量与使用参数autovacuum_vacuum_thresholdautovacuum_vacuum_scale_factor计算得出的值进行比较。清理阈值(autovacuum_vacuum_threshold)指定了用于比较的基础值,而清理比例因子(autovacuum_vacuum_scale_factor)则指定了在该基础值上要增加的表大小的比例。

这种策略确保了较小的表在经历较少的更新或删除操作后就会进行清理,而较大的表则需要更多的更新或删除操作才会清理。在许多情况下,这是合理的,但对于非常大的表来说,可能会导致清理频率过低。这是不理想的,原因有几个,比如非常大的表在两次清理之间会产生大量的膨胀。

这个新参数提供了一种方法,可以对使用autovacuum_vacuum_thresholdautovacuum_vacuum_scale_factor计算出的值设置一个上限,以让非常大的表更频繁地进行清理。默认情况下,该参数设置为 1 亿个元组,但可以通过将其设置为 - 1 来禁用它。也可以通过更改存储参数,针对单个表对其进行调整。

讨论:https://postgr.es/m/956435f8-3b2f-47a6-8756-8c54ded61802%40dalibo.com

示例

对于每个 PostgreSQL 数据库,清理和自动清理都是一个关键的部分。针对你的业务负载,如果自动清理配置不当,迟早会遇到数据膨胀和性能问题。大多数的 PostgreSQL 数据库,使用默认配置就能正常运行,很多人可能根本无需调整自动清理的任何参数。但另一方面,在有些业务负载下,默认的配置不是很适用,这时就需要调整自动清理处理特定表的方式。PostgreSQL 18 将引入一个名为 “autovacuum_vacuum_max_threshold” 的新参数,为解决特定问题提供了更多选择。

在介绍新参数之前,先看看默认配置下自动清理的触发时机。这由两个参数控制:

show autovacuum_vacuum_threshold;
 autovacuum_vacuum_threshold
-----------------------------
 50
(1 row)

show autovacuum_vacuum_scale_factor;
 autovacuum_vacuum_scale_factor
--------------------------------
 0.2
(1 row)

这意味着,在自动清理触发之前,大约需要表中 20% 的数据(即 autovacuum_vacuum_scale_factor 的 0.2 )加上 50 个元组(autovacuum_vacuum_threshold )发生变化。以这个拥有一百万行数据的简单表为例:

postgres=# \dt
                    Table "public.t"
  Column |  Type   | Collation | Nullable | Default 
---------+---------+-----------+----------+---------
  a      | integer |           |          | 
  b      | text    |           |          | 

postgres=# select count(*) from t;
  count
---------
 1000000
(1 row)

这样,只要更改表中超过 20% 的数据,就很容易触发自动清理:

select last_autovacuum from pg_stat_all_tables where relname = 't';
       last_autovacuum
-------------------------------
 2025-03-27 14:24:58.40076+08
(1 row)

select now();
              now
-------------------------------
 2025-03-27 14:26:48.333006+08
(1 row)

update t set b = 'xxx' where a < 250000;
UPDATE 249999

select pg_sleep('60');
 pg_sleep 
----------
 
(1 row)

select last_autovacuum from pg_stat_all_tables where relname = 't';
       last_autovacuum
-------------------------------
 2025-03-27 14:27:58.356337+08
(1 row)

这样做的结果是,表中的行数越多,自动清理的触发时间就越长。目前,你可以通过在表级别或者实例全局级别,调整 “autovacuum_vacuum_threshold” 或 “autovacuum_vacuum_scale_factor” 这两个参数,来解决这个问题。例如,如果你希望上面的表中,有 10000 行数据发生变化后就触发自动清理,可以这样操作:

alter table t set (autovacuum_vacuum_threshold = 10000);

alter table t set (autovacuum_vacuum_scale_factor = 0);

进行与上述相同的测试,但只更改 10001 行数据:

update t set b = 'aaa' where a < 10002;
UPDATE 10001

select now();
              now
-------------------------------
 2025-03-27 14:54:35.295413+08
(1 row)

select last_autovacuum from pg_stat_all_tables where relname = 't';
       last_autovacuum
-------------------------------
 2025-03-27 14:27:58.356337+08
(1 row)

select pg_sleep(60);
 pg_sleep 
----------
 
(1 row)

select last_autovacuum from pg_stat_all_tables where relname = 't';
       last_autovacuum
-------------------------------
 2025-03-27 14:54:58.69969+08
(1 row)

这种方式的缺点是,你需要手动处理这些设置。随着 “autovacuum_vacuum_max_threshold” 参数的引入,PostgreSQL 将以更加自动的方式来处理这些情况。这个参数的默认值相当高:

show autovacuum_vacuum_max_threshold;
 autovacuum_vacuum_max_threshold
--------------------------------
 100000000
(1 row)

为了看到它的实际效果,我们先重置上面设置的表级参数,然后设置 autovacuum_vacuum_max_threshold:

alter table t reset (autovacuum_vacuum_scale_factor);

alter table t reset (autovacuum_vacuum_threshold);

alter table t set (autovacuum_vacuum_max_threshold = 10000);

这将产生完全相同的效果:

update t set b = 'qqq' where a < 10002;
UPDATE 10001

select now();
              now
-------------------------------
 2025-03-27 15:02:51.582044+08
(1 row)

select last_autovacuum from pg_stat_all_tables where relname = 't';
       last_autovacuum
------------------------------
 2025-03-27 14:54:58.69969+08
(1 row)

select pg_sleep(60);
 pg_sleep
----------
 
(1 row)

select last_autovacuum from pg_stat_all_tables where relname = 't';
       last_autovacuum
------------------------------
 2025-03-27 15:03:11.99894+08
(1 row)

看起来确实很有用。感谢社区的所有相关人员。

参考

提交日志:https://git.postgresql.org/pg/commitdiff/306dc520b9dfd6014613961962a89940a431a069