From a7f06a93eb447139a8537f50a92cd97792039dd1 Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Mon, 21 Jan 2019 19:07:44 +0900 Subject: [PATCH v5] Add DISABLE_INDEX_CLEANUP option to VACUUM command --- doc/src/sgml/ref/vacuum.sgml | 20 +++++++++- src/backend/access/heap/vacuumlazy.c | 75 ++++++++++++++++++++++++++++-------- src/backend/commands/vacuum.c | 8 +++- src/backend/parser/gram.y | 2 + src/include/nodes/parsenodes.h | 4 +- src/test/regress/expected/vacuum.out | 4 ++ src/test/regress/sql/vacuum.sql | 3 ++ 7 files changed, 97 insertions(+), 19 deletions(-) diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index fd911f5..11ee59a 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -31,6 +31,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ table_and_columns is: @@ -161,7 +162,24 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ nonempty_pages = 0; vacrelstats->latestRemovedXid = InvalidTransactionId; - lazy_space_alloc(vacrelstats, nblocks); + /* + * Skip index vacuum if it's requested for table with indexes. In this + * case, we use the one-pass strategy and don't remove tuple storage. + */ + skip_index_vacuum = + (options & VACOPT_DISABLE_INDEX_CLEANUP) != 0 && vacrelstats->hasindex; + + lazy_space_alloc(vacrelstats, nblocks, skip_index_vacuum); frozen = palloc(sizeof(xl_heap_freeze_tuple) * MaxHeapTuplesPerPage); /* Report that we're scanning the heap, advertising total # of blocks */ @@ -723,6 +741,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, }; int64 hvp_val[2]; + Assert(!skip_index_vacuum); + /* * Before beginning index vacuuming, we release any pin we may * hold on the visibility map page. This isn't necessary for @@ -1204,12 +1224,23 @@ 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 (nindexes == 0 && + if ((nindexes == 0 || skip_index_vacuum) && vacrelstats->num_dead_tuples > 0) { - /* Remove tuples from heap */ - lazy_vacuum_page(onerel, blkno, buf, 0, vacrelstats, &vmbuffer); - has_dead_tuples = false; + /* + * Remove tuples from heap if the table has no index. If the table + * has index but index vacuum is disabled, we don't vacuum but forget + * them. The vacrelstats->dead_tuples could have tuples which became + * dead after checked at HOT-pruning time which are handled by + * lazy_vacuum_page() but we don't worry about handling those because + * it's a very rare condition and these would not be a large number. + */ + if (nindexes == 0) + { + Assert(!skip_index_vacuum); + lazy_vacuum_page(onerel, blkno, buf, 0, vacrelstats, &vmbuffer); + has_dead_tuples = false; + } /* * Forget the now-vacuumed tuples, and press on, but be careful @@ -1374,6 +1405,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, }; int64 hvp_val[2]; + Assert(!skip_index_vacuum); + /* Log cleanup info before we touch indexes */ vacuum_log_cleanup_info(onerel, vacrelstats); @@ -1412,15 +1445,24 @@ 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 (!skip_index_vacuum) + 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 */ + /* Make log report that lazy_vacuum_heap would've made */ if (vacuumed_pages) - ereport(elevel, - (errmsg("\"%s\": removed %.0f row versions in %u pages", - RelationGetRelationName(onerel), - tups_vacuumed, vacuumed_pages))); + { + if (skip_index_vacuum) + ereport(elevel, + (errmsg("\"%s\": marked %.0f row versions as dead in %u pages", + RelationGetRelationName(onerel), + tups_vacuumed, vacuumed_pages))); + else + ereport(elevel, + (errmsg("\"%s\": removed %.0f row versions in %u pages", + RelationGetRelationName(onerel), + tups_vacuumed, vacuumed_pages))); + } /* * This is pretty messy, but we split it up so that we can skip emitting @@ -2079,14 +2121,15 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats) * See the comments at the head of this file for rationale. */ static void -lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks) +lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks, + bool skip_index_vacuum) { long maxtuples; int vac_work_mem = IsAutoVacuumWorkerProcess() && autovacuum_work_mem != -1 ? autovacuum_work_mem : maintenance_work_mem; - if (vacrelstats->hasindex) + if (vacrelstats->hasindex && !skip_index_vacuum) { maxtuples = (vac_work_mem * 1024L) / sizeof(ItemPointerData); maxtuples = Min(maxtuples, INT_MAX); diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index c4522cd..a6f6fa0 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -204,7 +204,8 @@ vacuum(int options, List *relations, VacuumParams *params, stmttype))); /* - * Sanity check DISABLE_PAGE_SKIPPING option. + * Sanity check DISABLE_PAGE_SKIPPING option and DISABLE_INDEX_CLEANUP + * option. */ if ((options & VACOPT_FULL) != 0 && (options & VACOPT_DISABLE_PAGE_SKIPPING) != 0) @@ -212,6 +213,11 @@ vacuum(int options, List *relations, VacuumParams *params, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL"))); + if ((options & VACOPT_FULL) != 0 && + (options & VACOPT_DISABLE_INDEX_CLEANUP) != 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("VACUUM option DISABLE_INDEX_CLEANUP cannot be used with FULL"))); /* * Send info about dead objects to the statistics collector, unless we are * in autovacuum --- autovacuum.c does this for itself. diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index d8a3c2d..183a50d 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10519,6 +10519,8 @@ vacuum_option_elem: { if (strcmp($1, "disable_page_skipping") == 0) $$ = VACOPT_DISABLE_PAGE_SKIPPING; + else if (strcmp($1, "disable_index_cleanup") == 0) + $$ = VACOPT_DISABLE_INDEX_CLEANUP; else if (strcmp($1, "skip_locked") == 0) $$ = VACOPT_SKIP_LOCKED; else diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index addc2c2..91c6fa6 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3153,7 +3153,9 @@ typedef enum VacuumOption VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */ VACOPT_SKIP_LOCKED = 1 << 5, /* skip if cannot get lock */ VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */ - VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */ + VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7, /* don't skip any pages */ + VACOPT_DISABLE_INDEX_CLEANUP = 1 << 8 /* don't remove dead tuple and + * cleanup indexes */ } VacuumOption; /* diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out index fa9d663..402a8be 100644 --- a/src/test/regress/expected/vacuum.out +++ b/src/test/regress/expected/vacuum.out @@ -80,6 +80,10 @@ CONTEXT: SQL function "do_analyze" statement 1 SQL function "wrap_do_analyze" statement 1 VACUUM FULL vactst; VACUUM (DISABLE_PAGE_SKIPPING) vaccluster; +VACUUM (DISABLE_INDEX_CLEANUP) vaccluster; +VACUUM (DISABLE_INDEX_CLEANUP) vactst; -- DISABLE_INDEX_CLEANUP is ignored +NOTICE: DISABLE_INDEX_CLEANUP is ignored because table "vactst" does not have index +VACUUM (DISABLE_INDEX_CLEANUP, FREEZE) vaccluster; -- partitioned table CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a); CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1); diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql index 9defa0d..9c4bdb7 100644 --- a/src/test/regress/sql/vacuum.sql +++ b/src/test/regress/sql/vacuum.sql @@ -61,6 +61,9 @@ VACUUM FULL vaccluster; VACUUM FULL vactst; VACUUM (DISABLE_PAGE_SKIPPING) vaccluster; +VACUUM (DISABLE_INDEX_CLEANUP) vaccluster; +VACUUM (DISABLE_INDEX_CLEANUP) vactst; -- DISABLE_INDEX_CLEANUP is ignored +VACUUM (DISABLE_INDEX_CLEANUP, FREEZE) vaccluster; -- partitioned table CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a); -- 2.10.5