mirror of
https://github.com/postgres/postgres.git
synced 2025-07-05 07:21:24 +03:00
Allow VACUUM to be run with index cleanup disabled.
This commit adds a new reloption, vacuum_index_cleanup, which controls whether index cleanup is performed for a particular relation by default. It also adds a new option to the VACUUM command, INDEX_CLEANUP, which can be used to override the reloption. If neither the reloption nor the VACUUM option is used, the default is true, as before. Masahiko Sawada, reviewed and tested by Nathan Bossart, Alvaro Herrera, Kyotaro Horiguchi, Darafei Praliaskouski, and me. The wording of the documentation is mostly due to me. Discussion: http://postgr.es/m/CAD21AoAt5R3DNUZSjOoXDUY=naYPUOuffVsRzuTYMz29yLzQCA@mail.gmail.com
This commit is contained in:
@ -112,8 +112,8 @@
|
||||
|
||||
typedef struct LVRelStats
|
||||
{
|
||||
/* hasindex = true means two-pass strategy; false means one-pass */
|
||||
bool hasindex;
|
||||
/* useindex = true means two-pass strategy; false means one-pass */
|
||||
bool useindex;
|
||||
/* Overall statistics about rel */
|
||||
BlockNumber old_rel_pages; /* previous value of pg_class.relpages */
|
||||
BlockNumber rel_pages; /* total number of pages */
|
||||
@ -125,6 +125,8 @@ typedef struct LVRelStats
|
||||
double new_rel_tuples; /* new estimated total # of tuples */
|
||||
double new_live_tuples; /* new estimated total # of live tuples */
|
||||
double new_dead_tuples; /* new estimated total # of dead tuples */
|
||||
double nleft_dead_tuples; /* # of dead tuples we left */
|
||||
double nleft_dead_itemids; /* # of dead item pointers we left */
|
||||
BlockNumber pages_removed;
|
||||
double tuples_deleted;
|
||||
BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */
|
||||
@ -150,7 +152,7 @@ static BufferAccessStrategy vac_strategy;
|
||||
|
||||
|
||||
/* non-export function prototypes */
|
||||
static void lazy_scan_heap(Relation onerel, int options,
|
||||
static void lazy_scan_heap(Relation onerel, VacuumParams *params,
|
||||
LVRelStats *vacrelstats, Relation *Irel, int nindexes,
|
||||
bool aggressive);
|
||||
static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats, BlockNumber nblocks);
|
||||
@ -209,6 +211,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
|
||||
MultiXactId new_min_multi;
|
||||
|
||||
Assert(params != NULL);
|
||||
Assert(params->index_cleanup != VACOPT_TERNARY_DEFAULT);
|
||||
|
||||
/* measure elapsed time iff autovacuum logging requires it */
|
||||
if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
|
||||
@ -275,10 +278,11 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
|
||||
|
||||
/* Open all indexes of the relation */
|
||||
vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel);
|
||||
vacrelstats->hasindex = (nindexes > 0);
|
||||
vacrelstats->useindex = (nindexes > 0 &&
|
||||
params->index_cleanup == VACOPT_TERNARY_ENABLED);
|
||||
|
||||
/* Do the vacuuming */
|
||||
lazy_scan_heap(onerel, params->options, vacrelstats, Irel, nindexes, aggressive);
|
||||
lazy_scan_heap(onerel, params, vacrelstats, Irel, nindexes, aggressive);
|
||||
|
||||
/* Done with indexes */
|
||||
vac_close_indexes(nindexes, Irel, NoLock);
|
||||
@ -349,7 +353,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
|
||||
new_rel_pages,
|
||||
new_live_tuples,
|
||||
new_rel_allvisible,
|
||||
vacrelstats->hasindex,
|
||||
nindexes > 0,
|
||||
new_frozen_xid,
|
||||
new_min_multi,
|
||||
false);
|
||||
@ -419,6 +423,12 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
|
||||
vacrelstats->new_rel_tuples,
|
||||
vacrelstats->new_dead_tuples,
|
||||
OldestXmin);
|
||||
if (vacrelstats->nleft_dead_tuples > 0 ||
|
||||
vacrelstats->nleft_dead_itemids > 0)
|
||||
appendStringInfo(&buf,
|
||||
_("%.0f tuples and %.0f item identifiers are left as dead.\n"),
|
||||
vacrelstats->nleft_dead_tuples,
|
||||
vacrelstats->nleft_dead_itemids);
|
||||
appendStringInfo(&buf,
|
||||
_("buffer usage: %d hits, %d misses, %d dirtied\n"),
|
||||
VacuumPageHit,
|
||||
@ -485,7 +495,7 @@ vacuum_log_cleanup_info(Relation rel, LVRelStats *vacrelstats)
|
||||
* reference them have been killed.
|
||||
*/
|
||||
static void
|
||||
lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
lazy_scan_heap(Relation onerel, VacuumParams *params, LVRelStats *vacrelstats,
|
||||
Relation *Irel, int nindexes, bool aggressive)
|
||||
{
|
||||
BlockNumber nblocks,
|
||||
@ -501,7 +511,10 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
live_tuples, /* live tuples (reltuples estimate) */
|
||||
tups_vacuumed, /* tuples cleaned up by vacuum */
|
||||
nkeep, /* dead-but-not-removable tuples */
|
||||
nunused; /* unused item pointers */
|
||||
nunused, /* unused item pointers */
|
||||
nleft_dead_tuples, /* tuples we left as dead */
|
||||
nleft_dead_itemids; /* item pointers we left as dead,
|
||||
* includes nleft_dead_tuples. */
|
||||
IndexBulkDeleteResult **indstats;
|
||||
int i;
|
||||
PGRUsage ru0;
|
||||
@ -534,6 +547,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
empty_pages = vacuumed_pages = 0;
|
||||
next_fsm_block_to_vacuum = (BlockNumber) 0;
|
||||
num_tuples = live_tuples = tups_vacuumed = nkeep = nunused = 0;
|
||||
nleft_dead_itemids = nleft_dead_tuples = 0;
|
||||
|
||||
indstats = (IndexBulkDeleteResult **)
|
||||
palloc0(nindexes * sizeof(IndexBulkDeleteResult *));
|
||||
@ -599,7 +613,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
* be replayed on any hot standby, where it can be disruptive.
|
||||
*/
|
||||
next_unskippable_block = 0;
|
||||
if ((options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
|
||||
if ((params->options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
|
||||
{
|
||||
while (next_unskippable_block < nblocks)
|
||||
{
|
||||
@ -654,7 +668,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
{
|
||||
/* Time to advance next_unskippable_block */
|
||||
next_unskippable_block++;
|
||||
if ((options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
|
||||
if ((params->options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
|
||||
{
|
||||
while (next_unskippable_block < nblocks)
|
||||
{
|
||||
@ -1070,7 +1084,17 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
HeapTupleIsHeapOnly(&tuple))
|
||||
nkeep += 1;
|
||||
else
|
||||
{
|
||||
tupgone = true; /* we can delete the tuple */
|
||||
|
||||
/*
|
||||
* Since this dead tuple will not be vacuumed and
|
||||
* ignored when index cleanup is disabled we count
|
||||
* count it for reporting.
|
||||
*/
|
||||
if (params->index_cleanup == VACOPT_TERNARY_ENABLED)
|
||||
nleft_dead_tuples++;
|
||||
}
|
||||
all_visible = false;
|
||||
break;
|
||||
case HEAPTUPLE_LIVE:
|
||||
@ -1222,15 +1246,33 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are no indexes then we can vacuum the page right now
|
||||
* instead of doing a second scan.
|
||||
* If there are no indexes we can vacuum the page right now instead of
|
||||
* doing a second scan. Also we don't do that but forget dead tuples
|
||||
* when index cleanup is disabled.
|
||||
*/
|
||||
if (nindexes == 0 &&
|
||||
vacrelstats->num_dead_tuples > 0)
|
||||
if (!vacrelstats->useindex && vacrelstats->num_dead_tuples > 0)
|
||||
{
|
||||
/* Remove tuples from heap */
|
||||
lazy_vacuum_page(onerel, blkno, buf, 0, vacrelstats, &vmbuffer);
|
||||
has_dead_tuples = false;
|
||||
if (nindexes == 0)
|
||||
{
|
||||
/* Remove tuples from heap if the table has no index */
|
||||
lazy_vacuum_page(onerel, blkno, buf, 0, vacrelstats, &vmbuffer);
|
||||
vacuumed_pages++;
|
||||
has_dead_tuples = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Here, we have indexes but index cleanup is disabled. Instead of
|
||||
* vacuuming the dead tuples on the heap, we just forget them.
|
||||
*
|
||||
* Note that vacrelstats->dead_tuples could have tuples which
|
||||
* became dead after HOT-pruning but are not marked dead yet.
|
||||
* We do not process them because it's a very rare condition, and
|
||||
* the next vacuum will process them anyway.
|
||||
*/
|
||||
Assert(params->index_cleanup == VACOPT_TERNARY_DISABLED);
|
||||
nleft_dead_itemids += vacrelstats->num_dead_tuples;
|
||||
}
|
||||
|
||||
/*
|
||||
* Forget the now-vacuumed tuples, and press on, but be careful
|
||||
@ -1238,7 +1280,6 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
* valid.
|
||||
*/
|
||||
vacrelstats->num_dead_tuples = 0;
|
||||
vacuumed_pages++;
|
||||
|
||||
/*
|
||||
* Periodically do incremental FSM vacuuming to make newly-freed
|
||||
@ -1357,6 +1398,11 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
RecordPageWithFreeSpace(onerel, blkno, freespace, nblocks);
|
||||
}
|
||||
|
||||
/* No dead tuples should be left if index cleanup is enabled */
|
||||
Assert((params->index_cleanup == VACOPT_TERNARY_ENABLED &&
|
||||
nleft_dead_tuples == 0 && nleft_dead_itemids == 0) ||
|
||||
params->index_cleanup == VACOPT_TERNARY_DISABLED);
|
||||
|
||||
/* report that everything is scanned and vacuumed */
|
||||
pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno);
|
||||
|
||||
@ -1364,7 +1410,9 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
|
||||
/* save stats for use later */
|
||||
vacrelstats->tuples_deleted = tups_vacuumed;
|
||||
vacrelstats->new_dead_tuples = nkeep;
|
||||
vacrelstats->new_dead_tuples = nkeep + nleft_dead_tuples;
|
||||
vacrelstats->nleft_dead_tuples = nleft_dead_tuples;
|
||||
vacrelstats->nleft_dead_itemids = nleft_dead_itemids;
|
||||
|
||||
/* now we can compute the new value for pg_class.reltuples */
|
||||
vacrelstats->new_live_tuples = vac_estimate_reltuples(onerel,
|
||||
@ -1433,8 +1481,11 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
PROGRESS_VACUUM_PHASE_INDEX_CLEANUP);
|
||||
|
||||
/* Do post-vacuum cleanup and statistics update for each index */
|
||||
for (i = 0; i < nindexes; i++)
|
||||
lazy_cleanup_index(Irel[i], indstats[i], vacrelstats);
|
||||
if (vacrelstats->useindex)
|
||||
{
|
||||
for (i = 0; i < nindexes; i++)
|
||||
lazy_cleanup_index(Irel[i], indstats[i], vacrelstats);
|
||||
}
|
||||
|
||||
/* If no indexes, make log report that lazy_vacuum_heap would've made */
|
||||
if (vacuumed_pages)
|
||||
@ -1465,6 +1516,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
|
||||
"%u pages are entirely empty.\n",
|
||||
empty_pages),
|
||||
empty_pages);
|
||||
appendStringInfo(&buf, "%.0f tuples and %.0f item identifiers are left as dead.\n",
|
||||
nleft_dead_tuples, nleft_dead_itemids);
|
||||
appendStringInfo(&buf, _("%s."), pg_rusage_show(&ru0));
|
||||
|
||||
ereport(elevel,
|
||||
@ -2110,7 +2163,7 @@ lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks)
|
||||
autovacuum_work_mem != -1 ?
|
||||
autovacuum_work_mem : maintenance_work_mem;
|
||||
|
||||
if (vacrelstats->hasindex)
|
||||
if (vacrelstats->useindex)
|
||||
{
|
||||
maxtuples = (vac_work_mem * 1024L) / sizeof(ItemPointerData);
|
||||
maxtuples = Min(maxtuples, INT_MAX);
|
||||
|
Reference in New Issue
Block a user