Alvaro Herrera escribió:
> Okay, here's a patch along these lines. I haven't considered Jim's
> suggestion downthread about discounting dead tuples from relpages; maybe
> we can do that by subtracting the pages attributed to dead ones,
> estimating via tuple density (reltuples/relpages).
Patch attached.
--
Álvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
*** a/src/backend/postmaster/autovacuum.c
--- b/src/backend/postmaster/autovacuum.c
***************
*** 167,173 **** typedef struct avw_dbase
PgStat_StatDBEntry *adw_entry;
} avw_dbase;
! /* struct to keep track of tables to vacuum and/or analyze, in 1st pass */
typedef struct av_relation
{
Oid ar_toastrelid; /* hash key - must be first */
--- 167,173 ----
PgStat_StatDBEntry *adw_entry;
} avw_dbase;
! /* struct to keep track of TOAST<->main relation mappings */
typedef struct av_relation
{
Oid ar_toastrelid; /* hash key - must be first */
***************
*** 177,182 **** typedef struct av_relation
--- 177,201 ----
* reloptions, or NULL if none */
} av_relation;
+ /*
+ * A tasklist is a set of tables to process, collected during a worker's first
+ * phase. For each table we keep track of its Browne strength, so that we can
+ * process in priority order.
+ */
+ typedef struct avw_tltable
+ {
+ Oid tt_reloid;
+ float4 tt_browne_strength;
+ } avw_tltable;
+
+ typedef struct avw_tasklist
+ {
+ int tl_maxelts;
+ int tl_nelts;
+ avw_tltable **tl_elts;
+ } avw_tasklist;
+
+
/* struct to keep track of tables to vacuum and/or analyze, after rechecking */
typedef struct autovac_table
{
***************
*** 299,305 **** static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
Form_pg_class classForm,
PgStat_StatTabEntry *tabentry,
! bool *dovacuum, bool *doanalyze, bool *wraparound);
static void autovacuum_do_vac_analyze(autovac_table *tab,
BufferAccessStrategy bstrategy);
--- 318,326 ----
static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
Form_pg_class classForm,
PgStat_StatTabEntry *tabentry,
! bool *dovacuum, bool *doanalyze, bool *wraparound,
! float4 *deadtuples, float4 *relpages,
! uint32 *xidage);
static void autovacuum_do_vac_analyze(autovac_table *tab,
BufferAccessStrategy bstrategy);
***************
*** 1890,1895 **** get_database_list(void)
--- 1911,1979 ----
return dblist;
}
+ static avw_tasklist *
+ tasklist_initialize(void)
+ {
+ avw_tasklist *tasklist;
+
+ tasklist = palloc(sizeof(avw_tasklist));
+ tasklist->tl_maxelts = 32;
+ tasklist->tl_nelts = 0;
+ tasklist->tl_elts = palloc(tasklist->tl_maxelts * sizeof(avw_tltable *));
+
+ return tasklist;
+ }
+
+ /*
+ * Add a table to the tasklisk.
+ */
+ static void
+ tasklist_add_table(avw_tasklist *tasklist, Oid relid, bool dovacuum,
+ bool doanalyze, bool wraparound, float4 deadtuples,
+ float4 relpages, uint32 xidage)
+ {
+ avw_tltable *tab;
+
+ /* enlarge the array if necessary */
+ if (tasklist->tl_nelts >= tasklist->tl_maxelts)
+ {
+ tasklist->tl_maxelts *= 2;
+ tasklist->tl_elts = repalloc(tasklist->tl_elts, tasklist->tl_maxelts *
+ sizeof(avw_tltable *));
+ }
+
+ tab = palloc0(sizeof(avw_tltable));
+
+ tab->tt_reloid = relid;
+ if (dovacuum)
+ {
+ tab->tt_browne_strength = deadtuples / relpages +
+ exp(xidage * logf(relpages) / UINT_MAX);
+ }
+
+ tasklist->tl_elts[tasklist->tl_nelts++] = tab;
+ }
+
+ /*
+ * qsort comparator: sorts avw_tltable elements by value of Browne strength,
+ * descending
+ */
+ static int
+ avw_tt_compar(const void *a, const void *b)
+ {
+ const avw_tltable *taba = *(avw_tltable *const *) a;
+ const avw_tltable *tabb = *(avw_tltable *const *) b;
+
+ return tabb->tt_browne_strength - taba->tt_browne_strength;
+ }
+
+ static void
+ tasklist_sort(avw_tasklist *tasklist)
+ {
+ qsort(tasklist->tl_elts, tasklist->tl_nelts, sizeof(avw_tltable *),
+ avw_tt_compar);
+ }
+
/*
* Process a database table-by-table
*
***************
*** 1903,1917 **** do_autovacuum(void)
HeapTuple tuple;
HeapScanDesc relScan;
Form_pg_database dbForm;
- List *table_oids = NIL;
HASHCTL ctl;
HTAB *table_toast_map;
- ListCell *volatile cell;
PgStat_StatDBEntry *shared;
PgStat_StatDBEntry *dbentry;
BufferAccessStrategy bstrategy;
ScanKeyData key;
TupleDesc pg_class_desc;
/*
* StartTransactionCommand and CommitTransactionCommand will automatically
--- 1987,2001 ----
HeapTuple tuple;
HeapScanDesc relScan;
Form_pg_database dbForm;
HASHCTL ctl;
HTAB *table_toast_map;
PgStat_StatDBEntry *shared;
PgStat_StatDBEntry *dbentry;
BufferAccessStrategy bstrategy;
ScanKeyData key;
TupleDesc pg_class_desc;
+ avw_tasklist *tasklist;
+ int i;
/*
* StartTransactionCommand and CommitTransactionCommand will automatically
***************
*** 1986,1991 **** do_autovacuum(void)
--- 2070,2078 ----
&ctl,
HASH_ELEM | HASH_FUNCTION);
+ /* initialize our tasklist */
+ tasklist = tasklist_initialize();
+
/*
* Scan pg_class to determine which tables to vacuum.
*
***************
*** 2020,2025 **** do_autovacuum(void)
--- 2107,2115 ----
bool dovacuum;
bool doanalyze;
bool wraparound;
+ float4 deadtuples;
+ float4 relpages;
+ uint32 xidage;
relid = HeapTupleGetOid(tuple);
***************
*** 2030,2036 **** do_autovacuum(void)
/* Check if it needs vacuum or analyze */
relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
! &dovacuum, &doanalyze, &wraparound);
/*
* Check if it is a temp table (presumably, of some other backend's).
--- 2120,2127 ----
/* Check if it needs vacuum or analyze */
relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
! &dovacuum, &doanalyze, &wraparound,
! &deadtuples, &relpages, &xidage);
/*
* Check if it is a temp table (presumably, of some other backend's).
***************
*** 2077,2085 **** do_autovacuum(void)
}
else
{
! /* relations that need work are added to table_oids */
if (dovacuum || doanalyze)
! table_oids = lappend_oid(table_oids, relid);
/*
* Remember the association for the second pass. Note: we must do
--- 2168,2177 ----
}
else
{
! /* relations that need work are added to our tasklist */
if (dovacuum || doanalyze)
! tasklist_add_table(tasklist, relid, dovacuum, doanalyze,
! wraparound, deadtuples, relpages, xidage);
/*
* Remember the association for the second pass. Note: we must do
***************
*** 2129,2134 **** do_autovacuum(void)
--- 2221,2229 ----
bool dovacuum;
bool doanalyze;
bool wraparound;
+ float4 deadtuples;
+ float4 relpages;
+ uint32 xidage;
/*
* We cannot safely process other backends' temp tables, so skip 'em.
***************
*** 2158,2168 **** do_autovacuum(void)
shared, dbentry);
relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
! &dovacuum, &doanalyze, &wraparound);
/* ignore analyze for toast tables */
if (dovacuum)
! table_oids = lappend_oid(table_oids, relid);
}
heap_endscan(relScan);
--- 2253,2265 ----
shared, dbentry);
relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
! &dovacuum, &doanalyze, &wraparound,
! &deadtuples, &relpages, &xidage);
/* ignore analyze for toast tables */
if (dovacuum)
! tasklist_add_table(tasklist, relid, dovacuum, doanalyze,
! wraparound, deadtuples, relpages, xidage);
}
heap_endscan(relScan);
***************
*** 2185,2202 **** do_autovacuum(void)
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
/*
* Perform operations on collected tables.
*/
! foreach(cell, table_oids)
{
- Oid relid = lfirst_oid(cell);
autovac_table *tab;
bool skipit;
int stdVacuumCostDelay;
int stdVacuumCostLimit;
dlist_iter iter;
CHECK_FOR_INTERRUPTS();
/*
--- 2282,2304 ----
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
+ /* sort our task list */
+ tasklist_sort(tasklist);
+
/*
* Perform operations on collected tables.
*/
! for (i = 0; i < tasklist->tl_nelts; i++)
{
autovac_table *tab;
+ Oid relid;
bool skipit;
int stdVacuumCostDelay;
int stdVacuumCostLimit;
dlist_iter iter;
+ relid = tasklist->tl_elts[i]->tt_reloid;
+
CHECK_FOR_INTERRUPTS();
/*
***************
*** 2499,2505 **** table_recheck_autovac(Oid relid, HTAB *table_toast_map,
shared, dbentry);
relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
! &dovacuum, &doanalyze, &wraparound);
/* ignore ANALYZE for toast tables */
if (classForm->relkind == RELKIND_TOASTVALUE)
--- 2601,2608 ----
shared, dbentry);
relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
! &dovacuum, &doanalyze, &wraparound,
! NULL, NULL, NULL);
/* ignore ANALYZE for toast tables */
if (classForm->relkind == RELKIND_TOASTVALUE)
***************
*** 2565,2573 **** table_recheck_autovac(Oid relid, HTAB *table_toast_map,
/*
* relation_needs_vacanalyze
*
! * Check whether a relation needs to be vacuumed or analyzed; return each into
! * "dovacuum" and "doanalyze", respectively. Also return whether the vacuum is
! * being forced because of Xid wraparound.
*
* relopts is a pointer to the AutoVacOpts options (either for itself in the
* case of a plain table, or for either itself or its parent table in the case
--- 2668,2682 ----
/*
* relation_needs_vacanalyze
*
! * Check whether a relation needs to be vacuumed or analyzed, and return each
! * into "dovacuum" and "doanalyze", respectively.
! *
! * Output parameters:
! * wraparound: whether the vacuum is being forced because of Xid wraparound
! * deadtuples: number of dead tuples in the table (from the pgstat tabentry;
! * zero if table is not present in pgstat)
! * relpages: number of pages in the table (from pg_class)
! * xidage: age of relfrozenxid, compared to recentXid
*
* relopts is a pointer to the AutoVacOpts options (either for itself in the
* case of a plain table, or for either itself or its parent table in the case
***************
*** 2606,2612 **** relation_needs_vacanalyze(Oid relid,
/* output params below */
bool *dovacuum,
bool *doanalyze,
! bool *wraparound)
{
bool force_vacuum;
bool av_enabled;
--- 2715,2724 ----
/* output params below */
bool *dovacuum,
bool *doanalyze,
! bool *wraparound,
! float4 *deadtuples,
! float4 *relpages,
! uint32 *xidage)
{
bool force_vacuum;
bool av_enabled;
***************
*** 2715,2724 **** relation_needs_vacanalyze(Oid relid,
/*
* Skip a table not found in stat hash, unless we have to force vacuum
* for anti-wrap purposes. If it's not acted upon, there's no need to
! * vacuum it.
*/
*dovacuum = force_vacuum;
*doanalyze = false;
}
/* ANALYZE refuses to work with pg_statistics */
--- 2827,2848 ----
/*
* Skip a table not found in stat hash, unless we have to force vacuum
* for anti-wrap purposes. If it's not acted upon, there's no need to
! * vacuum it. It seems okay to say that there are no tuples to remove,
! * either.
*/
*dovacuum = force_vacuum;
*doanalyze = false;
+ vactuples = 0;
+ }
+
+ if (*dovacuum)
+ {
+ if (deadtuples)
+ *deadtuples = vactuples;
+ if (relpages)
+ *relpages = classForm->relpages;
+ if (xidage)
+ *xidage = recentXid - classForm->relfrozenxid;
}
/* ANALYZE refuses to work with pg_statistics */
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers