mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Optimize vacuuming of relations with no indexes.
If there are no indexes on a relation, items can be marked LP_UNUSED instead of LP_DEAD when pruning. This significantly reduces WAL volume, since we no longer need to emit one WAL record for pruning and a second to change the LP_DEAD line pointers thus created to LP_UNUSED. Melanie Plageman, reviewed by Andres Freund, Peter Geoghegan, and me Discussion: https://postgr.es/m/CAAKRu_bgvb_k0gKOXWzNKWHt560R0smrGe3E8zewKPs8fiMKkw%40mail.gmail.com
This commit is contained in:
@ -1036,69 +1036,6 @@ lazy_scan_heap(LVRelState *vacrel)
|
||||
|
||||
Assert(!prunestate.all_visible || !prunestate.has_lpdead_items);
|
||||
|
||||
if (vacrel->nindexes == 0)
|
||||
{
|
||||
/*
|
||||
* Consider the need to do page-at-a-time heap vacuuming when
|
||||
* using the one-pass strategy now.
|
||||
*
|
||||
* The one-pass strategy will never call lazy_vacuum(). The steps
|
||||
* performed here can be thought of as the one-pass equivalent of
|
||||
* a call to lazy_vacuum().
|
||||
*/
|
||||
if (prunestate.has_lpdead_items)
|
||||
{
|
||||
Size freespace;
|
||||
|
||||
lazy_vacuum_heap_page(vacrel, blkno, buf, 0, vmbuffer);
|
||||
|
||||
/* Forget the LP_DEAD items that we just vacuumed */
|
||||
dead_items->num_items = 0;
|
||||
|
||||
/*
|
||||
* Now perform FSM processing for blkno, and move on to next
|
||||
* page.
|
||||
*
|
||||
* Our call to lazy_vacuum_heap_page() will have considered if
|
||||
* it's possible to set all_visible/all_frozen independently
|
||||
* of lazy_scan_prune(). Note that prunestate was invalidated
|
||||
* by lazy_vacuum_heap_page() call.
|
||||
*/
|
||||
freespace = PageGetHeapFreeSpace(page);
|
||||
|
||||
UnlockReleaseBuffer(buf);
|
||||
RecordPageWithFreeSpace(vacrel->rel, blkno, freespace);
|
||||
|
||||
/*
|
||||
* Periodically perform FSM vacuuming to make newly-freed
|
||||
* space visible on upper FSM pages. FreeSpaceMapVacuumRange()
|
||||
* vacuums the portion of the freespace map covering heap
|
||||
* pages from start to end - 1. Include the block we just
|
||||
* vacuumed by passing it blkno + 1. Overflow isn't an issue
|
||||
* because MaxBlockNumber + 1 is InvalidBlockNumber which
|
||||
* causes FreeSpaceMapVacuumRange() to vacuum freespace map
|
||||
* pages covering the remainder of the relation.
|
||||
*/
|
||||
if (blkno - next_fsm_block_to_vacuum >= VACUUM_FSM_EVERY_PAGES)
|
||||
{
|
||||
FreeSpaceMapVacuumRange(vacrel->rel, next_fsm_block_to_vacuum,
|
||||
blkno + 1);
|
||||
next_fsm_block_to_vacuum = blkno + 1;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* There was no call to lazy_vacuum_heap_page() because pruning
|
||||
* didn't encounter/create any LP_DEAD items that needed to be
|
||||
* vacuumed. Prune state has not been invalidated, so proceed
|
||||
* with prunestate-driven visibility map and FSM steps (just like
|
||||
* the two-pass strategy).
|
||||
*/
|
||||
Assert(dead_items->num_items == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle setting visibility map bit based on information from the VM
|
||||
* (as of last lazy_scan_skip() call), and from prunestate
|
||||
@ -1209,38 +1146,45 @@ lazy_scan_heap(LVRelState *vacrel)
|
||||
|
||||
/*
|
||||
* Final steps for block: drop cleanup lock, record free space in the
|
||||
* FSM
|
||||
* FSM.
|
||||
*
|
||||
* If we will likely do index vacuuming, wait until
|
||||
* lazy_vacuum_heap_rel() to save free space. This doesn't just save
|
||||
* us some cycles; it also allows us to record any additional free
|
||||
* space that lazy_vacuum_heap_page() will make available in cases
|
||||
* where it's possible to truncate the page's line pointer array.
|
||||
*
|
||||
* Note: It's not in fact 100% certain that we really will call
|
||||
* lazy_vacuum_heap_rel() -- lazy_vacuum() might yet opt to skip index
|
||||
* vacuuming (and so must skip heap vacuuming). This is deemed okay
|
||||
* because it only happens in emergencies, or when there is very
|
||||
* little free space anyway. (Besides, we start recording free space
|
||||
* in the FSM once index vacuuming has been abandoned.)
|
||||
*/
|
||||
if (prunestate.has_lpdead_items && vacrel->do_index_vacuuming)
|
||||
{
|
||||
/*
|
||||
* Wait until lazy_vacuum_heap_rel() to save free space. This
|
||||
* doesn't just save us some cycles; it also allows us to record
|
||||
* any additional free space that lazy_vacuum_heap_page() will
|
||||
* make available in cases where it's possible to truncate the
|
||||
* page's line pointer array.
|
||||
*
|
||||
* Note: It's not in fact 100% certain that we really will call
|
||||
* lazy_vacuum_heap_rel() -- lazy_vacuum() might yet opt to skip
|
||||
* index vacuuming (and so must skip heap vacuuming). This is
|
||||
* deemed okay because it only happens in emergencies, or when
|
||||
* there is very little free space anyway. (Besides, we start
|
||||
* recording free space in the FSM once index vacuuming has been
|
||||
* abandoned.)
|
||||
*
|
||||
* Note: The one-pass (no indexes) case is only supposed to make
|
||||
* it this far when there were no LP_DEAD items during pruning.
|
||||
*/
|
||||
Assert(vacrel->nindexes > 0);
|
||||
UnlockReleaseBuffer(buf);
|
||||
}
|
||||
else
|
||||
if (vacrel->nindexes == 0
|
||||
|| !vacrel->do_index_vacuuming
|
||||
|| !prunestate.has_lpdead_items)
|
||||
{
|
||||
Size freespace = PageGetHeapFreeSpace(page);
|
||||
|
||||
UnlockReleaseBuffer(buf);
|
||||
RecordPageWithFreeSpace(vacrel->rel, blkno, freespace);
|
||||
|
||||
/*
|
||||
* Periodically perform FSM vacuuming to make newly-freed space
|
||||
* visible on upper FSM pages. This is done after vacuuming if the
|
||||
* table has indexes.
|
||||
*/
|
||||
if (vacrel->nindexes == 0 && prunestate.has_lpdead_items &&
|
||||
blkno - next_fsm_block_to_vacuum >= VACUUM_FSM_EVERY_PAGES)
|
||||
{
|
||||
FreeSpaceMapVacuumRange(vacrel->rel, next_fsm_block_to_vacuum,
|
||||
blkno);
|
||||
next_fsm_block_to_vacuum = blkno;
|
||||
}
|
||||
}
|
||||
else
|
||||
UnlockReleaseBuffer(buf);
|
||||
}
|
||||
|
||||
vacrel->blkno = InvalidBlockNumber;
|
||||
@ -1596,8 +1540,13 @@ lazy_scan_prune(LVRelState *vacrel,
|
||||
* in presult.ndeleted. It should not be confused with lpdead_items;
|
||||
* lpdead_items's final value can be thought of as the number of tuples
|
||||
* that were deleted from indexes.
|
||||
*
|
||||
* If the relation has no indexes, we can immediately mark would-be dead
|
||||
* items LP_UNUSED, so mark_unused_now should be true if no indexes and
|
||||
* false otherwise.
|
||||
*/
|
||||
heap_page_prune(rel, buf, vacrel->vistest, &presult, &vacrel->offnum);
|
||||
heap_page_prune(rel, buf, vacrel->vistest, vacrel->nindexes == 0,
|
||||
&presult, &vacrel->offnum);
|
||||
|
||||
/*
|
||||
* Now scan the page to collect LP_DEAD items and check for tuples
|
||||
@ -2520,7 +2469,7 @@ lazy_vacuum_heap_page(LVRelState *vacrel, BlockNumber blkno, Buffer buffer,
|
||||
bool all_frozen;
|
||||
LVSavedErrInfo saved_err_info;
|
||||
|
||||
Assert(vacrel->nindexes == 0 || vacrel->do_index_vacuuming);
|
||||
Assert(vacrel->do_index_vacuuming);
|
||||
|
||||
pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno);
|
||||
|
||||
|
Reference in New Issue
Block a user