mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Truncate line pointer array during VACUUM.
Teach VACUUM to truncate the line pointer array of each heap page when a contiguous group of LP_UNUSED line pointers appear at the end of the array -- these unused and unreferenced items are excluded. This process occurs during VACUUM's second pass over the heap, right after LP_DEAD line pointers on the page (those encountered/pruned during the first pass) are marked LP_UNUSED. Truncation avoids line pointer bloat with certain workloads, particularly those involving continual range DELETEs and bulk INSERTs against the same table. Also harden heapam code to check for an out-of-range page offset number in places where we weren't already doing so. Author: Matthias van de Meent <boekewurm+postgres@gmail.com> Author: Peter Geoghegan <pg@bowt.ie> Reviewed-By: Masahiko Sawada <sawada.mshk@gmail.com> Reviewed-By: Peter Geoghegan <pg@bowt.ie> Discussion: https://postgr.es/m/CAEze2WjgaQc55Y5f5CQd3L=eS5CZcff2Obxp=O6pto8-f0hC4w@mail.gmail.com Discussion: https://postgr.es/m/CAH2-Wzn6a64PJM1Ggzm=uvx2otsopJMhFQj_g1rAj4GWr3ZSzw@mail.gmail.com
This commit is contained in:
@ -635,8 +635,15 @@ heapgettup(HeapScanDesc scan,
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The previous returned tuple may have been vacuumed since the
|
||||
* previous scan when we use a non-MVCC snapshot, so we must
|
||||
* re-establish the lineoff <= PageGetMaxOffsetNumber(dp)
|
||||
* invariant
|
||||
*/
|
||||
lineoff = /* previous offnum */
|
||||
OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self)));
|
||||
Min(lines,
|
||||
OffsetNumberPrev(ItemPointerGetOffsetNumber(&(tuple->t_self))));
|
||||
}
|
||||
/* page and lineoff now reference the physically previous tid */
|
||||
|
||||
@ -678,6 +685,13 @@ heapgettup(HeapScanDesc scan,
|
||||
lpp = PageGetItemId(dp, lineoff);
|
||||
for (;;)
|
||||
{
|
||||
/*
|
||||
* Only continue scanning the page while we have lines left.
|
||||
*
|
||||
* Note that this protects us from accessing line pointers past
|
||||
* PageGetMaxOffsetNumber(); both for forward scans when we resume the
|
||||
* table scan, and for when we start scanning a new page.
|
||||
*/
|
||||
while (linesleft > 0)
|
||||
{
|
||||
if (ItemIdIsNormal(lpp))
|
||||
@ -8556,10 +8570,8 @@ heap_xlog_vacuum(XLogReaderState *record)
|
||||
ItemIdSetUnused(lp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the page's hint bit about whether it has free pointers
|
||||
*/
|
||||
PageSetHasFreeLinePointers(page);
|
||||
/* Attempt to truncate line pointer array now */
|
||||
PageTruncateLinePointerArray(page);
|
||||
|
||||
PageSetLSN(page, lsn);
|
||||
MarkBufferDirty(buffer);
|
||||
|
@ -962,6 +962,10 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
/* Sanity check */
|
||||
if (nextoffnum < FirstOffsetNumber || nextoffnum > maxoff)
|
||||
break;
|
||||
|
||||
lp = PageGetItemId(page, nextoffnum);
|
||||
|
||||
/* Check for broken chains */
|
||||
|
@ -1444,7 +1444,11 @@ lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
|
||||
if (prunestate.has_lpdead_items && vacrel->do_index_vacuuming)
|
||||
{
|
||||
/*
|
||||
* Wait until lazy_vacuum_heap_rel() to save free space.
|
||||
* 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: The one-pass (no indexes) case is only supposed to make
|
||||
* it this far when there were no LP_DEAD items during pruning.
|
||||
@ -2033,6 +2037,13 @@ lazy_vacuum_all_indexes(LVRelState *vacrel)
|
||||
* Pages that never had lazy_scan_prune record LP_DEAD items are not visited
|
||||
* at all.
|
||||
*
|
||||
* We may also be able to truncate the line pointer array of the heap pages we
|
||||
* visit. If there is a contiguous group of LP_UNUSED items at the end of the
|
||||
* array, it can be reclaimed as free space. These LP_UNUSED items usually
|
||||
* start out as LP_DEAD items recorded by lazy_scan_prune (we set items from
|
||||
* each page to LP_UNUSED, and then consider if it's possible to truncate the
|
||||
* page's line pointer array).
|
||||
*
|
||||
* Note: the reason for doing this as a second pass is we cannot remove the
|
||||
* tuples until we've removed their index entries, and we want to process
|
||||
* index entry removal in batches as large as possible.
|
||||
@ -2175,7 +2186,8 @@ lazy_vacuum_heap_page(LVRelState *vacrel, BlockNumber blkno, Buffer buffer,
|
||||
|
||||
Assert(uncnt > 0);
|
||||
|
||||
PageSetHasFreeLinePointers(page);
|
||||
/* Attempt to truncate line pointer array now */
|
||||
PageTruncateLinePointerArray(page);
|
||||
|
||||
/*
|
||||
* Mark buffer dirty before we write WAL.
|
||||
|
Reference in New Issue
Block a user