Introduce autovacuum_vacuum_max_threshold.
authorNathan Bossart <nathan@postgresql.org>
Wed, 5 Feb 2025 21:48:18 +0000 (15:48 -0600)
committerNathan Bossart <nathan@postgresql.org>
Wed, 5 Feb 2025 21:48:18 +0000 (15:48 -0600)
One way autovacuum chooses tables to vacuum is by comparing the
number of updated or deleted tuples with a value calculated using
autovacuum_vacuum_threshold and autovacuum_vacuum_scale_factor.
The threshold specifies the base value for comparison, and the
scale factor specifies the fraction of the table size to add to it.
This strategy ensures that smaller tables are vacuumed after fewer
updates/deletes than larger tables, which is reasonable in many
cases but can result in infrequent vacuums on very large tables.
This is undesirable for a couple of reasons, such as very large
tables incurring a huge amount of bloat between vacuums.

This new parameter provides a way to set a limit on the value
calculated with autovacuum_vacuum_threshold and
autovacuum_vacuum_scale_factor so that very large tables are
vacuumed more frequently.  By default, it is set to 100,000,000
tuples, but it can be disabled by setting it to -1.  It can also be
adjusted for individual tables by changing storage parameters.

Author: Nathan Bossart <nathandbossart@gmail.com>
Co-authored-by: Frédéric Yhuel <frederic.yhuel@dalibo.com>
Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Reviewed-by: Laurenz Albe <laurenz.albe@cybertec.at>
Reviewed-by: Michael Banck <mbanck@gmx.net>
Reviewed-by: Joe Conway <mail@joeconway.com>
Reviewed-by: Sami Imseih <samimseih@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: wenhui qiu <qiuwenhuifx@gmail.com>
Reviewed-by: Vinícius Abrahão <vinnix.bsd@gmail.com>
Reviewed-by: Robert Treat <rob@xzilla.net>
Reviewed-by: Alena Rybakina <a.rybakina@postgrespro.ru>
Discussion: https://postgr.es/m/956435f8-3b2f-47a6-8756-8c54ded61802%40dalibo.com

doc/src/sgml/config.sgml
doc/src/sgml/maintenance.sgml
doc/src/sgml/ref/create_table.sgml
src/backend/access/common/reloptions.c
src/backend/postmaster/autovacuum.c
src/backend/utils/misc/guc_tables.c
src/backend/utils/misc/postgresql.conf.sample
src/bin/psql/tab-complete.in.c
src/include/postmaster/autovacuum.h
src/include/utils/rel.h

index a782f109982433556388d554a6a180a603f15902..38244409e3c900753d1579af35df4da9d6a4682a 100644 (file)
@@ -8685,6 +8685,30 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
        </listitem>
       </varlistentry>
 
+      <varlistentry id="guc-autovacuum-vacuum-max-threshold" xreflabel="autovacuum_vacuum_max_threshold">
+       <term><varname>autovacuum_vacuum_max_threshold</varname> (<type>integer</type>)
+       <indexterm>
+        <primary><varname>autovacuum_vacuum_max_threshold</varname></primary>
+        <secondary>configuration parameter</secondary>
+       </indexterm>
+       </term>
+       <listitem>
+        <para>
+         Specifies the maximum number of updated or deleted tuples needed to
+         trigger a <command>VACUUM</command> in any one table, i.e., a limit on
+         the value calculated with
+         <varname>autovacuum_vacuum_threshold</varname> and
+         <varname>autovacuum_vacuum_scale_factor</varname>.  The default is
+         100,000,000 tuples.  If -1 is specified, autovacuum will not enforce a
+         maximum number of updated or deleted tuples that will trigger a
+         <command>VACUUM</command> operation.  This parameter can only be set
+         in the <filename>postgresql.conf</filename> file or on the server
+         command line; but the setting can be overridden for individual tables
+         by changing storage parameters.
+        </para>
+       </listitem>
+      </varlistentry>
+
       <varlistentry id="guc-autovacuum-freeze-max-age" xreflabel="autovacuum_freeze_max_age">
        <term><varname>autovacuum_freeze_max_age</varname> (<type>integer</type>)
        <indexterm>
index 0be90bdc7efe1b3aeda5ecf3bbd88315675d6b45..f84ad7557d9d5dbf26aa8640454c54e8afbbb4f4 100644 (file)
@@ -895,9 +895,11 @@ HINT:  Execute a database-wide VACUUM in that database.
     <command>VACUUM</command> exceeds the <quote>vacuum threshold</quote>, the
     table is vacuumed.  The vacuum threshold is defined as:
 <programlisting>
-vacuum threshold = vacuum base threshold + vacuum scale factor * number of tuples
+vacuum threshold = Minimum(vacuum max threshold, vacuum base threshold + vacuum scale factor * number of tuples)
 </programlisting>
-    where the vacuum base threshold is
+    where the vacuum max threshold is
+    <xref linkend="guc-autovacuum-vacuum-max-threshold"/>,
+    the vacuum base threshold is
     <xref linkend="guc-autovacuum-vacuum-threshold"/>,
     the vacuum scale factor is
     <xref linkend="guc-autovacuum-vacuum-scale-factor"/>,
index 2237321cb4f4ece6a50ac4064f2cbafaf07c78fc..417498f71db9a17ab1584837271dc21497855419 100644 (file)
@@ -1712,6 +1712,21 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
     </listitem>
    </varlistentry>
 
+   <varlistentry id="reloption-autovacuum-vacuum-max-threshold" xreflabel="autovacuum_vacuum_max_threshold">
+    <term><literal>autovacuum_vacuum_max_threshold</literal>, <literal>toast.autovacuum_vacuum_max_threshold</literal> (<type>integer</type>)
+    <indexterm>
+     <primary><varname>autovacuum_vacuum_max_threshold</varname></primary>
+     <secondary>storage parameter</secondary>
+    </indexterm>
+    </term>
+   <listitem>
+    <para>
+     Per-table value for <xref linkend="guc-autovacuum-vacuum-max-threshold"/>
+     parameter.
+    </para>
+   </listitem>
+  </varlistentry>
+
    <varlistentry id="reloption-autovacuum-vacuum-scale-factor" xreflabel="autovacuum_vacuum_scale_factor">
     <term><literal>autovacuum_vacuum_scale_factor</literal>, <literal>toast.autovacuum_vacuum_scale_factor</literal> (<type>floating point</type>)
     <indexterm>
index e587abd99904f3c00044c01d95dc1228edd710e1..5731cf42f5492296c44b4249e2f267dcfddd6081 100644 (file)
@@ -231,6 +231,15 @@ static relopt_int intRelOpts[] =
        },
        -1, 0, INT_MAX
    },
+   {
+       {
+           "autovacuum_vacuum_max_threshold",
+           "Maximum number of tuple updates or deletes prior to vacuum",
+           RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+           ShareUpdateExclusiveLock
+       },
+       -2, -1, INT_MAX
+   },
    {
        {
            "autovacuum_vacuum_insert_threshold",
@@ -1843,6 +1852,8 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
        {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
+       {"autovacuum_vacuum_max_threshold", RELOPT_TYPE_INT,
+       offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_max_threshold)},
        {"autovacuum_vacuum_insert_threshold", RELOPT_TYPE_INT,
        offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_threshold)},
        {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
index 0ab921a169b850f63ff5aa1ebb3f69e60060a01b..09ec9bb6990e346e55f6519c5ec79c3956d22388 100644 (file)
@@ -120,6 +120,7 @@ int         autovacuum_max_workers;
 int            autovacuum_work_mem = -1;
 int            autovacuum_naptime;
 int            autovacuum_vac_thresh;
+int            autovacuum_vac_max_thresh;
 double     autovacuum_vac_scale;
 int            autovacuum_vac_ins_thresh;
 double     autovacuum_vac_ins_scale;
@@ -2895,6 +2896,8 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * threshold.  This threshold is calculated as
  *
  * threshold = vac_base_thresh + vac_scale_factor * reltuples
+ * if (threshold > vac_max_thresh)
+ *     threshold = vac_max_thresh;
  *
  * For analyze, the analysis done is that the number of tuples inserted,
  * deleted and updated since the last analyze exceeds a threshold calculated
@@ -2933,6 +2936,7 @@ relation_needs_vacanalyze(Oid relid,
 
    /* constants from reloptions or GUC variables */
    int         vac_base_thresh,
+               vac_max_thresh,
                vac_ins_base_thresh,
                anl_base_thresh;
    float4      vac_scale_factor,
@@ -2974,6 +2978,11 @@ relation_needs_vacanalyze(Oid relid,
        ? relopts->vacuum_threshold
        : autovacuum_vac_thresh;
 
+   /* -1 is used to disable max threshold */
+   vac_max_thresh = (relopts && relopts->vacuum_max_threshold >= -1)
+       ? relopts->vacuum_max_threshold
+       : autovacuum_vac_max_thresh;
+
    vac_ins_scale_factor = (relopts && relopts->vacuum_ins_scale_factor >= 0)
        ? relopts->vacuum_ins_scale_factor
        : autovacuum_vac_ins_scale;
@@ -3047,6 +3056,9 @@ relation_needs_vacanalyze(Oid relid,
            reltuples = 0;
 
        vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples;
+       if (vac_max_thresh >= 0 && vacthresh > (float4) vac_max_thresh)
+           vacthresh = (float4) vac_max_thresh;
+
        vacinsthresh = (float4) vac_ins_base_thresh + vac_ins_scale_factor * reltuples;
        anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
 
index 71448bb4fdd5f1743f885a0d60c5230ec64bbb59..b887d3e59839877745b98457a055c0459126ba8e 100644 (file)
@@ -3426,6 +3426,15 @@ struct config_int ConfigureNamesInt[] =
        50, 0, INT_MAX,
        NULL, NULL, NULL
    },
+   {
+       {"autovacuum_vacuum_max_threshold", PGC_SIGHUP, VACUUM_AUTOVACUUM,
+           gettext_noop("Maximum number of tuple updates or deletes prior to vacuum."),
+           NULL
+       },
+       &autovacuum_vac_max_thresh,
+       100000000, -1, INT_MAX,
+       NULL, NULL, NULL
+   },
    {
        {"autovacuum_vacuum_insert_threshold", PGC_SIGHUP, VACUUM_AUTOVACUUM,
            gettext_noop("Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums."),
index 079efa1baa799eb0bc08c3f163dab8ae57607f65..c40b7a3121e7f39a7f79b74d374d5eb060f79ee1 100644 (file)
@@ -670,6 +670,9 @@ autovacuum_worker_slots = 16    # autovacuum worker slots to allocate
 #autovacuum_vacuum_insert_scale_factor = 0.2   # fraction of inserts over table
                        # size before insert vacuum
 #autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
+#autovacuum_vacuum_max_threshold = 100000000    # max number of row updates
+                       # before vacuum; -1 disables max
+                       # threshold
 #autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
                    # (change requires restart)
 #autovacuum_multixact_freeze_max_age = 400000000   # maximum multixact age
index 81cbf10aa28763912c8eb2a66c2943d57b865fa3..5f6897c84866ca6568498083e4abcfbeb50404f9 100644 (file)
@@ -1368,6 +1368,7 @@ static const char *const table_storage_parameters[] = {
    "autovacuum_vacuum_cost_limit",
    "autovacuum_vacuum_insert_scale_factor",
    "autovacuum_vacuum_insert_threshold",
+   "autovacuum_vacuum_max_threshold",
    "autovacuum_vacuum_scale_factor",
    "autovacuum_vacuum_threshold",
    "fillfactor",
@@ -1384,6 +1385,7 @@ static const char *const table_storage_parameters[] = {
    "toast.autovacuum_vacuum_cost_limit",
    "toast.autovacuum_vacuum_insert_scale_factor",
    "toast.autovacuum_vacuum_insert_threshold",
+   "toast.autovacuum_vacuum_max_threshold",
    "toast.autovacuum_vacuum_scale_factor",
    "toast.autovacuum_vacuum_threshold",
    "toast.log_autovacuum_min_duration",
index 54e01c81d68e534b3a1b5de4c4b6c5b98963001d..06d4a593575882f0cc24836293b85a27824cb2e6 100644 (file)
@@ -33,6 +33,7 @@ extern PGDLLIMPORT int autovacuum_max_workers;
 extern PGDLLIMPORT int autovacuum_work_mem;
 extern PGDLLIMPORT int autovacuum_naptime;
 extern PGDLLIMPORT int autovacuum_vac_thresh;
+extern PGDLLIMPORT int autovacuum_vac_max_thresh;
 extern PGDLLIMPORT double autovacuum_vac_scale;
 extern PGDLLIMPORT int autovacuum_vac_ins_thresh;
 extern PGDLLIMPORT double autovacuum_vac_ins_scale;
index 33d1e4a4e2e0409d781267be1fc04891db28c9e5..48b95f211f37215be10abb77a69a1648f0cc1cbe 100644 (file)
@@ -309,6 +309,7 @@ typedef struct AutoVacOpts
 {
    bool        enabled;
    int         vacuum_threshold;
+   int         vacuum_max_threshold;
    int         vacuum_ins_threshold;
    int         analyze_threshold;
    int         vacuum_cost_limit;