diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/access/common/reloptions.c | 10 | ||||
-rw-r--r-- | src/backend/access/heap/heapam.c | 1 | ||||
-rw-r--r-- | src/backend/access/heap/vacuumlazy.c | 43 | ||||
-rw-r--r-- | src/backend/commands/vacuum.c | 25 | ||||
-rw-r--r-- | src/backend/postmaster/autovacuum.c | 10 | ||||
-rw-r--r-- | src/backend/utils/misc/guc_tables.c | 14 | ||||
-rw-r--r-- | src/backend/utils/misc/postgresql.conf.sample | 1 | ||||
-rw-r--r-- | src/include/commands/vacuum.h | 12 | ||||
-rw-r--r-- | src/include/utils/rel.h | 1 |
9 files changed, 115 insertions, 2 deletions
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index 14c23101adf..dfa802416fc 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -314,6 +314,14 @@ static relopt_int intRelOpts[] = }, { { + "autovacuum_freeze_strategy_threshold", + "Table size at which VACUUM freezes using eager strategy, in megabytes.", + RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, + ShareUpdateExclusiveLock + }, -1, 0, MAX_KILOBYTES + }, + { + { "log_autovacuum_min_duration", "Sets the minimum execution time above which autovacuum actions will be logged", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, @@ -1863,6 +1871,8 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind) offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)}, {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)}, + {"autovacuum_freeze_strategy_threshold", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_strategy_threshold)}, {"log_autovacuum_min_duration", RELOPT_TYPE_INT, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)}, {"toast_tuple_target", RELOPT_TYPE_INT, diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index e6024a980bb..fec041d6cdd 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -7057,6 +7057,7 @@ heap_freeze_tuple(HeapTupleHeader tuple, cutoffs.OldestMxact = MultiXactCutoff; cutoffs.FreezeLimit = FreezeLimit; cutoffs.MultiXactCutoff = MultiXactCutoff; + cutoffs.freeze_strategy_threshold_pages = 0; pagefrz.freeze_required = true; pagefrz.FreezePageRelfrozenXid = FreezeLimit; diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 8f14cf85f38..e3f86493b90 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -153,6 +153,8 @@ typedef struct LVRelState bool aggressive; /* Use visibility map to skip? (disabled by DISABLE_PAGE_SKIPPING) */ bool skipwithvm; + /* Eagerly freeze pages that are eligible to become all-frozen? */ + bool eager_freeze_strategy; /* Wraparound failsafe has been triggered? */ bool failsafe_active; /* Consider index vacuuming bypass optimization? */ @@ -243,6 +245,7 @@ typedef struct LVSavedErrInfo /* non-export function prototypes */ static void lazy_scan_heap(LVRelState *vacrel); +static void lazy_scan_strategy(LVRelState *vacrel); static BlockNumber lazy_scan_skip(LVRelState *vacrel, Buffer *vmbuffer, BlockNumber next_block, bool *next_unskippable_allvis, @@ -472,6 +475,10 @@ heap_vacuum_rel(Relation rel, VacuumParams *params, vacrel->skipwithvm = skipwithvm; + /* + * Now determine VACUUM's freezing strategy + */ + lazy_scan_strategy(vacrel); if (verbose) { if (vacrel->aggressive) @@ -1268,6 +1275,38 @@ lazy_scan_heap(LVRelState *vacrel) } /* + * lazy_scan_strategy() -- Determine freezing strategy. + * + * Our lazy freezing strategy is useful when putting off the work of freezing + * totally avoids freezing that turns out to have been wasted effort later on. + * Our eager freezing strategy is useful with larger tables that experience + * continual growth, where freezing pages proactively is needed just to avoid + * falling behind on freezing (eagerness is also likely to be cheaper in the + * short/medium term for such tables, but the long term picture matters most). + */ +static void +lazy_scan_strategy(LVRelState *vacrel) +{ + BlockNumber rel_pages = vacrel->rel_pages; + + /* + * Decide freezing strategy. + * + * The eager freezing strategy is used whenever rel_pages exceeds a + * threshold controlled by the freeze_strategy_threshold GUC/reloption. + * + * Also freeze eagerly with an unlogged or temp table, where the total + * cost of freezing pages is mostly just the cycles needed to prepare a + * set of freeze plans. Executing the freeze plans adds very little cost. + * Dirtying extra pages isn't a concern, either; VACUUM will definitely + * set PD_ALL_VISIBLE on affected pages, regardless of freezing strategy. + */ + vacrel->eager_freeze_strategy = + (rel_pages > vacrel->cutoffs.freeze_strategy_threshold_pages || + !RelationIsPermanent(vacrel->rel)); +} + +/* * lazy_scan_skip() -- set up range of skippable blocks using visibility map. * * lazy_scan_heap() calls here every time it needs to set up a new range of @@ -1795,10 +1834,12 @@ retry: * one XID/MXID from before FreezeLimit/MultiXactCutoff is present. Also * freeze when pruning generated an FPI, if doing so means that we set the * page all-frozen afterwards (might not happen until final heap pass). + * When ongoing VACUUM opted to use the eager freezing strategy we freeze + * any page that will thereby become all-frozen in the visibility map. */ if (pagefrz.freeze_required || tuples_frozen == 0 || (prunestate->all_visible && prunestate->all_frozen && - fpi_before != pgWalUsage.wal_fpi)) + (fpi_before != pgWalUsage.wal_fpi || vacrel->eager_freeze_strategy))) { /* * We're freezing the page. Our final NewRelfrozenXid doesn't need to diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 7b1a4b127eb..033a9db90ce 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -68,6 +68,7 @@ int vacuum_freeze_min_age; int vacuum_freeze_table_age; int vacuum_multixact_freeze_min_age; int vacuum_multixact_freeze_table_age; +int vacuum_freeze_strategy_threshold; int vacuum_failsafe_age; int vacuum_multixact_failsafe_age; @@ -264,6 +265,7 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel) params.freeze_table_age = 0; params.multixact_freeze_min_age = 0; params.multixact_freeze_table_age = 0; + params.freeze_strategy_threshold = 0; } else { @@ -271,6 +273,7 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel) params.freeze_table_age = -1; params.multixact_freeze_min_age = -1; params.multixact_freeze_table_age = -1; + params.freeze_strategy_threshold = -1; } /* user-invoked vacuum is never "for wraparound" */ @@ -962,7 +965,9 @@ vacuum_get_cutoffs(Relation rel, const VacuumParams *params, multixact_freeze_min_age, freeze_table_age, multixact_freeze_table_age, - effective_multixact_freeze_max_age; + effective_multixact_freeze_max_age, + freeze_strategy_threshold; + uint64 threshold_strategy_pages; TransactionId nextXID, safeOldestXmin, aggressiveXIDCutoff; @@ -975,6 +980,7 @@ vacuum_get_cutoffs(Relation rel, const VacuumParams *params, multixact_freeze_min_age = params->multixact_freeze_min_age; freeze_table_age = params->freeze_table_age; multixact_freeze_table_age = params->multixact_freeze_table_age; + freeze_strategy_threshold = params->freeze_strategy_threshold; /* Set pg_class fields in cutoffs */ cutoffs->relfrozenxid = rel->rd_rel->relfrozenxid; @@ -1090,6 +1096,23 @@ vacuum_get_cutoffs(Relation rel, const VacuumParams *params, cutoffs->MultiXactCutoff = cutoffs->OldestMxact; /* + * Determine the freeze_strategy_threshold to use: as specified by the + * caller, or vacuum_freeze_strategy_threshold + */ + if (freeze_strategy_threshold < 0) + freeze_strategy_threshold = vacuum_freeze_strategy_threshold; + Assert(freeze_strategy_threshold >= 0); + + /* + * Convert MB-based freeze_strategy_threshold to page-based value used by + * our vacuumlazy.c caller, while being careful to avoid overflow + */ + threshold_strategy_pages = + ((uint64) freeze_strategy_threshold * 1024 * 1024) / BLCKSZ; + threshold_strategy_pages = Min(threshold_strategy_pages, MaxBlockNumber); + cutoffs->freeze_strategy_threshold_pages = threshold_strategy_pages; + + /* * Finally, figure out if caller needs to do an aggressive VACUUM or not. * * Determine the table freeze age to use: as specified by the caller, or diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index f5ea381c53e..ecddde3a1ea 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -151,6 +151,7 @@ static int default_freeze_min_age; static int default_freeze_table_age; static int default_multixact_freeze_min_age; static int default_multixact_freeze_table_age; +static int default_freeze_strategy_threshold; /* Memory context for long-lived data */ static MemoryContext AutovacMemCxt; @@ -2010,6 +2011,7 @@ do_autovacuum(void) default_freeze_table_age = 0; default_multixact_freeze_min_age = 0; default_multixact_freeze_table_age = 0; + default_freeze_strategy_threshold = 0; } else { @@ -2017,6 +2019,7 @@ do_autovacuum(void) default_freeze_table_age = vacuum_freeze_table_age; default_multixact_freeze_min_age = vacuum_multixact_freeze_min_age; default_multixact_freeze_table_age = vacuum_multixact_freeze_table_age; + default_freeze_strategy_threshold = vacuum_freeze_strategy_threshold; } ReleaseSysCache(tuple); @@ -2801,6 +2804,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, int freeze_table_age; int multixact_freeze_min_age; int multixact_freeze_table_age; + int freeze_strategy_threshold; int vac_cost_limit; double vac_cost_delay; int log_min_duration; @@ -2850,6 +2854,11 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, ? avopts->multixact_freeze_table_age : default_multixact_freeze_table_age; + freeze_strategy_threshold = (avopts && + avopts->freeze_strategy_threshold >= 0) + ? avopts->freeze_strategy_threshold + : default_freeze_strategy_threshold; + tab = palloc(sizeof(autovac_table)); tab->at_relid = relid; tab->at_sharedrel = classForm->relisshared; @@ -2877,6 +2886,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, tab->at_params.freeze_table_age = freeze_table_age; tab->at_params.multixact_freeze_min_age = multixact_freeze_min_age; tab->at_params.multixact_freeze_table_age = multixact_freeze_table_age; + tab->at_params.freeze_strategy_threshold = freeze_strategy_threshold; tab->at_params.is_wraparound = wraparound; tab->at_params.log_min_duration = log_min_duration; tab->at_vacuum_cost_limit = vac_cost_limit; diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 4ac808ed224..dcfa0c4c4b3 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -2536,6 +2536,20 @@ struct config_int ConfigureNamesInt[] = }, { + {"vacuum_freeze_strategy_threshold", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Table size at which VACUUM freezes using eager strategy, in megabytes."), + gettext_noop("This is applied by comparing it to the size of a table's main fork at " + "the beginning of each VACUUM. Eager freezing strategy is used when size " + "exceeds the threshold, or when table is a temporary or unlogged table. " + "Otherwise lazy freezing strategy is used."), + GUC_UNIT_MB + }, + &vacuum_freeze_strategy_threshold, + 4096, 0, MAX_KILOBYTES, + NULL, NULL, NULL + }, + + { {"vacuum_defer_cleanup_age", PGC_SIGHUP, REPLICATION_PRIMARY, gettext_noop("Number of transactions by which VACUUM and HOT cleanup should be deferred, if any."), NULL diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index d06074b86f6..fda695e7567 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -700,6 +700,7 @@ #vacuum_multixact_freeze_table_age = 150000000 #vacuum_multixact_freeze_min_age = 5000000 #vacuum_multixact_failsafe_age = 1600000000 +#vacuum_freeze_strategy_threshold = 4GB #bytea_output = 'hex' # hex, escape #xmlbinary = 'base64' #xmloption = 'content' diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 689dbb77024..2a4dae7dd87 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -222,6 +222,9 @@ typedef struct VacuumParams * use default */ int multixact_freeze_table_age; /* multixact age at which to scan * whole table */ + int freeze_strategy_threshold; /* threshold to use eager + * freezing, in megabytes, -1 to + * use default */ bool is_wraparound; /* force a for-wraparound vacuum */ int log_min_duration; /* minimum execution threshold in ms at * which autovacuum is logged, -1 to use @@ -274,6 +277,14 @@ struct VacuumCutoffs */ TransactionId FreezeLimit; MultiXactId MultiXactCutoff; + + /* + * Eager freezing strategy is used whenever target rel's main fork size + * exceeds freeze_strategy_threshold_pages. Otherwise lazy freezing + * strategy is used. (Actually, there are exceptions. Non-permanent + * tables always use eager freezing strategy.) + */ + BlockNumber freeze_strategy_threshold_pages; }; /* @@ -297,6 +308,7 @@ extern PGDLLIMPORT int vacuum_freeze_min_age; extern PGDLLIMPORT int vacuum_freeze_table_age; extern PGDLLIMPORT int vacuum_multixact_freeze_min_age; extern PGDLLIMPORT int vacuum_multixact_freeze_table_age; +extern PGDLLIMPORT int vacuum_freeze_strategy_threshold; extern PGDLLIMPORT int vacuum_failsafe_age; extern PGDLLIMPORT int vacuum_multixact_failsafe_age; diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index af9785038d3..39c7ccf0c89 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -314,6 +314,7 @@ typedef struct AutoVacOpts int multixact_freeze_min_age; int multixact_freeze_max_age; int multixact_freeze_table_age; + int freeze_strategy_threshold; int log_min_duration; float8 vacuum_cost_delay; float8 vacuum_scale_factor; |