From 31bcb2ed8f9ba0f5e5d9b6c2e03cbebf7ef65f40 Mon Sep 17 00:00:00 2001 From: Junwang Zhao Date: Wed, 30 Apr 2025 10:20:16 +0000 Subject: [PATCH 1/2] Introduce autovacuum vacuum strategy Add a new configuration option `autovacuum_vacuum_strategy` to control the order in which tables are vacuumed by the autovacuum process. Two strategies are supported for now: 1. Sequential: Tables are vacuumed in the order they are collected. 2. Random: The list of tables is rotated around a randomly chosen pivot before vacuuming to avoid always starting with the same table, which prevents vacuuming starvation for some tables. The default strategy is `sequential`. Signed-off-by: Junwang Zhao --- src/backend/postmaster/autovacuum.c | 33 +++++++++++++++++++ src/backend/utils/misc/guc_tables.c | 10 ++++++ src/backend/utils/misc/postgresql.conf.sample | 1 + src/include/postmaster/autovacuum.h | 7 ++++ src/include/utils/guc.h | 1 + 5 files changed, 52 insertions(+) diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index ff96b36d7102..5bd3ff6ed988 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -79,6 +79,7 @@ #include "catalog/pg_namespace.h" #include "commands/dbcommands.h" #include "commands/vacuum.h" +#include "common/pg_prng.h" #include "common/int.h" #include "lib/ilist.h" #include "libpq/pqsignal.h" @@ -132,9 +133,16 @@ int autovacuum_multixact_freeze_max_age; double autovacuum_vac_cost_delay; int autovacuum_vac_cost_limit; +int autovacuum_vac_strategy; int Log_autovacuum_min_duration = 600000; +const struct config_enum_entry autovacuum_vac_strategy_options[] = { + {"sequential", AUTOVACUUM_VAC_STRATEGY_SEQUENTIAL, false}, + {"random", AUTOVACUUM_VAC_STRATEGY_RANDOM, false}, + {NULL, 0, false} +}; + /* the minimum allowed time between two awakenings of the launcher */ #define MIN_AUTOVAC_SLEEPTIME 100.0 /* milliseconds */ #define MAX_AUTOVAC_SLEEPTIME 300 /* seconds */ @@ -2299,6 +2307,30 @@ do_autovacuum(void) "Autovacuum Portal", ALLOCSET_DEFAULT_SIZES); + /* + * Randomly rotate the list of tables to vacuum. This is to avoid always + * vacuuming the same table first, which could lead to spinning on the + * same table or vacuuming starvation. + */ + if (list_length(table_oids) > 1 && autovacuum_vac_strategy == AUTOVACUUM_VAC_STRATEGY_RANDOM) + { + + int rand = 0; + static pg_prng_state prng_state; + List *tmp_oids = NIL; + + pg_prng_seed(&prng_state, (uint64) (getpid() ^ time(NULL))); + rand = (int) pg_prng_uint64_range(&prng_state, 0, + list_length(table_oids) - 1); + if (rand != 0) + { + tmp_oids = list_copy_tail(table_oids, rand); + table_oids = list_copy_head(table_oids, + list_length(table_oids) - rand); + table_oids = list_concat(table_oids, tmp_oids); + } + } + /* * Perform operations on collected tables. */ @@ -3339,6 +3371,7 @@ AutoVacuumRequestWork(AutoVacuumWorkItemType type, Oid relationId, workitem->avw_used = true; workitem->avw_active = false; workitem->avw_type = type; + workitem->avw_database = MyDatabaseId; workitem->avw_relation = relationId; workitem->avw_blockNumber = blkno; diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index d14b1678e7fe..47d1ecaaf8ad 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -5418,6 +5418,16 @@ struct config_enum ConfigureNamesEnum[] = NULL, assign_io_method, NULL }, + { + {"autovacuum_vacuum_strategy", PGC_SIGHUP, VACUUM_AUTOVACUUM, + gettext_noop("Vacuum strategy for autovacuum."), + NULL + }, + &autovacuum_vac_strategy, + AUTOVACUUM_VAC_STRATEGY_SEQUENTIAL, autovacuum_vac_strategy_options, + NULL, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index a9d8293474af..fec4df3b0f17 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -709,6 +709,7 @@ autovacuum_worker_slots = 16 # autovacuum worker slots to allocate #autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for # autovacuum, -1 means use # vacuum_cost_limit +#autovacuum_vacuum_strategy = sequential # - Cost-Based Vacuum Delay - diff --git a/src/include/postmaster/autovacuum.h b/src/include/postmaster/autovacuum.h index e8135f41a1c2..218ea4ee1c83 100644 --- a/src/include/postmaster/autovacuum.h +++ b/src/include/postmaster/autovacuum.h @@ -25,6 +25,12 @@ typedef enum AVW_BRINSummarizeRange, } AutoVacuumWorkItemType; +/* Autovacuum vacuum strategies */ +enum AutovacuumVacStrategy +{ + AUTOVACUUM_VAC_STRATEGY_SEQUENTIAL = 0, + AUTOVACUUM_VAC_STRATEGY_RANDOM, +}; /* GUC variables */ extern PGDLLIMPORT bool autovacuum_start_daemon; @@ -43,6 +49,7 @@ extern PGDLLIMPORT int autovacuum_freeze_max_age; extern PGDLLIMPORT int autovacuum_multixact_freeze_max_age; extern PGDLLIMPORT double autovacuum_vac_cost_delay; extern PGDLLIMPORT int autovacuum_vac_cost_limit; +extern PGDLLIMPORT int autovacuum_vac_strategy; /* autovacuum launcher PID, only valid when worker is shutting down */ extern PGDLLIMPORT int AutovacuumLauncherPid; diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index f619100467df..2f5c5363b035 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -322,6 +322,7 @@ extern PGDLLIMPORT const struct config_enum_entry io_method_options[]; extern PGDLLIMPORT const struct config_enum_entry recovery_target_action_options[]; extern PGDLLIMPORT const struct config_enum_entry wal_level_options[]; extern PGDLLIMPORT const struct config_enum_entry wal_sync_method_options[]; +extern PGDLLIMPORT const struct config_enum_entry autovacuum_vac_strategy_options[]; /* * Functions exported by guc.c From caafe98ef06e6d793d2ac5c9d7d764141d9b5ae5 Mon Sep 17 00:00:00 2001 From: Junwang Zhao Date: Thu, 1 May 2025 10:14:08 +0000 Subject: [PATCH 2/2] skip vacuuming the table if last time vacuum percent less than 10 --- src/backend/postmaster/autovacuum.c | 16 ++++++++++++++++ src/backend/utils/activity/pgstat_relation.c | 7 +++++++ src/include/pgstat.h | 1 + 3 files changed, 24 insertions(+) diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 5bd3ff6ed988..c64d71793331 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -3146,6 +3146,22 @@ relation_needs_vacanalyze(Oid relid, float4 reltuples = classForm->reltuples; int32 relpages = classForm->relpages; int32 relallfrozen = classForm->relallfrozen; + float4 last_remove_tuples_percent = tabentry->last_autovacuum_removed_tuples_percent; + TimestampTz last_autovacuum_time = tabentry->last_autovacuum_time; + + /* + * If the last autovacuum removed tuples is less than 10% of the + * current dead tuples, then skip vacuuming this table for some time. + */ + if (!TimestampDifferenceExceedsSeconds(last_autovacuum_time, + GetCurrentTimestamp(), + autovacuum_naptime) && + last_remove_tuples_percent < 10.0) + { + *doanalyze = false; + *dovacuum = false; + return; + } vactuples = tabentry->dead_tuples; instuples = tabentry->ins_since_vacuum; diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c index 69df741cbf63..d6ba1bf89e38 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -217,6 +217,7 @@ pgstat_report_vacuum(Oid tableoid, bool shared, Oid dboid = (shared ? InvalidOid : MyDatabaseId); TimestampTz ts; PgStat_Counter elapsedtime; + PgStat_Counter old_dead_tuples; if (!pgstat_track_counts) return; @@ -232,6 +233,7 @@ pgstat_report_vacuum(Oid tableoid, bool shared, shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats; tabentry = &shtabentry->stats; + old_dead_tuples = tabentry->dead_tuples; tabentry->live_tuples = livetuples; tabentry->dead_tuples = deadtuples; @@ -250,6 +252,11 @@ pgstat_report_vacuum(Oid tableoid, bool shared, if (AmAutoVacuumWorkerProcess()) { tabentry->last_autovacuum_time = ts; + if (old_dead_tuples > deadtuples) + tabentry->last_autovacuum_removed_tuples_percent = + 100 * (old_dead_tuples - deadtuples) / old_dead_tuples; + else + tabentry->last_autovacuum_removed_tuples_percent = 0; tabentry->autovacuum_count++; tabentry->total_autovacuum_time += elapsedtime; } diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 202bd2d5aced..b0a36ff01337 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -443,6 +443,7 @@ typedef struct PgStat_StatTabEntry TimestampTz last_vacuum_time; /* user initiated vacuum */ PgStat_Counter vacuum_count; TimestampTz last_autovacuum_time; /* autovacuum initiated */ + PgStat_Counter last_autovacuum_removed_tuples_percent; PgStat_Counter autovacuum_count; TimestampTz last_analyze_time; /* user initiated */ PgStat_Counter analyze_count;