mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Deduplicate freeze plans in freeze WAL records.
Make heapam WAL records that describe freezing performed by VACUUM more space efficient by storing each distinct "freeze plan" once, alongside an array of associated page offset numbers (one per freeze plan). The freeze plans required for most heap pages tend to naturally have a great deal of redundancy, so this technique is very effective in practice. It often leads to freeze WAL records that are less than 20% of the size of equivalent WAL records generated using the previous approach. The freeze plan concept was introduced by commit3b97e6823b
, which fixed bugs in VACUUM's handling of MultiXacts. We retain the concept of freeze plans, but go back to using page offset number arrays. There is no loss of generality here because deduplication is an additive process that gets applied mechanically when FREEZE_PAGE WAL records are built. More than anything else, freeze plan deduplication is an optimization that reduces the marginal cost of freezing additional tuples on pages that will need to have at least one or two tuples frozen in any case. Ongoing work that adds page-level freezing to VACUUM will take full advantage of the improved cost profile through batching. Also refactor some of the details surrounding recovery conflicts needed to REDO freeze records in passing: make original execution responsible for generating a standard latestRemovedXid cutoff, rather than working backwards to get the same cutoff in the REDO routine. Bugfix commit66fbcb0d2e
did it the other way around, which is equivalent but obscures what's going on. Also rename the cutoff field from the WAL record/struct (rename the field cutoff_xid to latestRemovedXid to match similar WAL records). Processing of conflicts by REDO routines is already completely uniform, so tools like pg_waldump should present the information driving the process uniformly. There are two remaining WAL record types that still don't quite follow this convention (heapam's VISIBLE record type and SP-GiST's VACUUM_REDIRECT record type). They can be brought into line by later work that totally standardizes how the cutoffs are presented. Bump XLOG_PAGE_MAGIC. Author: Peter Geoghegan <pg@bowt.ie> Reviewed-By: Masahiko Sawada <sawada.mshk@gmail.com> Reviewed-By: Nathan Bossart <nathandbossart@gmail.com> Reviewed-By: Justin Pryzby <pryzby@telsasoft.com> Discussion: https://postgr.es/m/CAH2-Wz=XytErMnb8FAyFd+OQEbiipB0Q2FmFdXrggPL4VBnRYQ@mail.gmail.com
This commit is contained in:
@ -1566,7 +1566,7 @@ lazy_scan_prune(LVRelState *vacrel,
|
||||
TransactionId NewRelfrozenXid;
|
||||
MultiXactId NewRelminMxid;
|
||||
OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
|
||||
xl_heap_freeze_tuple frozen[MaxHeapTuplesPerPage];
|
||||
HeapTupleFreeze frozen[MaxHeapTuplesPerPage];
|
||||
|
||||
Assert(BufferGetBlockNumber(buf) == blkno);
|
||||
|
||||
@ -1776,13 +1776,9 @@ retry:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-removable tuple (i.e. tuple with storage).
|
||||
*
|
||||
* Check tuple left behind after pruning to see if needs to be frozen
|
||||
* now.
|
||||
*/
|
||||
prunestate->hastup = true; /* page makes rel truncation unsafe */
|
||||
|
||||
/* Tuple with storage -- consider need to freeze */
|
||||
if (heap_prepare_freeze_tuple(tuple.t_data,
|
||||
vacrel->relfrozenxid,
|
||||
vacrel->relminmxid,
|
||||
@ -1792,7 +1788,7 @@ retry:
|
||||
&tuple_totally_frozen,
|
||||
&NewRelfrozenXid, &NewRelminMxid))
|
||||
{
|
||||
/* Will execute freeze below */
|
||||
/* Save prepared freeze plan for later */
|
||||
frozen[tuples_frozen++].offset = offnum;
|
||||
}
|
||||
|
||||
@ -1825,40 +1821,9 @@ retry:
|
||||
|
||||
vacrel->frozen_pages++;
|
||||
|
||||
/*
|
||||
* At least one tuple with storage needs to be frozen -- execute that
|
||||
* now.
|
||||
*
|
||||
* If we need to freeze any tuples we'll mark the buffer dirty, and
|
||||
* write a WAL record recording the changes. We must log the changes
|
||||
* to be crash-safe against future truncation of CLOG.
|
||||
*/
|
||||
START_CRIT_SECTION();
|
||||
|
||||
MarkBufferDirty(buf);
|
||||
|
||||
/* execute collected freezes */
|
||||
for (int i = 0; i < tuples_frozen; i++)
|
||||
{
|
||||
HeapTupleHeader htup;
|
||||
|
||||
itemid = PageGetItemId(page, frozen[i].offset);
|
||||
htup = (HeapTupleHeader) PageGetItem(page, itemid);
|
||||
|
||||
heap_execute_freeze_tuple(htup, &frozen[i]);
|
||||
}
|
||||
|
||||
/* Now WAL-log freezing if necessary */
|
||||
if (RelationNeedsWAL(vacrel->rel))
|
||||
{
|
||||
XLogRecPtr recptr;
|
||||
|
||||
recptr = log_heap_freeze(vacrel->rel, buf, vacrel->FreezeLimit,
|
||||
/* Execute all freeze plans for page as a single atomic action */
|
||||
heap_freeze_execute_prepared(vacrel->rel, buf, vacrel->FreezeLimit,
|
||||
frozen, tuples_frozen);
|
||||
PageSetLSN(page, recptr);
|
||||
}
|
||||
|
||||
END_CRIT_SECTION();
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user