|
3 | 3 | * vacuumlazy.c
|
4 | 4 | * Concurrent ("lazy") vacuuming.
|
5 | 5 | *
|
6 |
| - * |
7 |
| - * The major space usage for LAZY VACUUM is storage for the array of dead tuple |
8 |
| - * TIDs. We want to ensure we can vacuum even the very largest relations with |
9 |
| - * finite memory space usage. To do that, we set upper bounds on the number of |
10 |
| - * tuples we will keep track of at once. |
| 6 | + * The major space usage for vacuuming is storage for the array of dead TIDs |
| 7 | + * that are to be removed from indexes. We want to ensure we can vacuum even |
| 8 | + * the very largest relations with finite memory space usage. To do that, we |
| 9 | + * set upper bounds on the number of TIDs we can keep track of at once. |
11 | 10 | *
|
12 | 11 | * We are willing to use at most maintenance_work_mem (or perhaps
|
13 |
| - * autovacuum_work_mem) memory space to keep track of dead tuples. We |
14 |
| - * initially allocate an array of TIDs of that size, with an upper limit that |
15 |
| - * depends on table size (this limit ensures we don't allocate a huge area |
16 |
| - * uselessly for vacuuming small tables). If the array threatens to overflow, |
17 |
| - * we suspend the heap scan phase and perform a pass of index cleanup and page |
18 |
| - * compaction, then resume the heap scan with an empty TID array. |
19 |
| - * |
20 |
| - * If we're processing a table with no indexes, we can just vacuum each page |
21 |
| - * as we go; there's no need to save up multiple tuples to minimize the number |
22 |
| - * of index scans performed. So we don't use maintenance_work_mem memory for |
23 |
| - * the TID array, just enough to hold as many heap tuples as fit on one page. |
| 12 | + * autovacuum_work_mem) memory space to keep track of dead TIDs. We initially |
| 13 | + * allocate an array of TIDs of that size, with an upper limit that depends on |
| 14 | + * table size (this limit ensures we don't allocate a huge area uselessly for |
| 15 | + * vacuuming small tables). If the array threatens to overflow, we must call |
| 16 | + * lazy_vacuum to vacuum indexes (and to vacuum the pages that we've pruned). |
| 17 | + * This frees up the memory space dedicated to storing dead TIDs. |
24 | 18 | *
|
25 |
| - * Lazy vacuum supports parallel execution with parallel worker processes. In |
26 |
| - * a parallel vacuum, we perform both index vacuum and index cleanup with |
27 |
| - * parallel worker processes. Individual indexes are processed by one vacuum |
28 |
| - * process. At the beginning of a lazy vacuum (at lazy_scan_heap) we prepare |
29 |
| - * the parallel context and initialize the DSM segment that contains shared |
30 |
| - * information as well as the memory space for storing dead tuples. When |
31 |
| - * starting either index vacuum or index cleanup, we launch parallel worker |
32 |
| - * processes. Once all indexes are processed the parallel worker processes |
33 |
| - * exit. After that, the leader process re-initializes the parallel context |
34 |
| - * so that it can use the same DSM for multiple passes of index vacuum and |
35 |
| - * for performing index cleanup. For updating the index statistics, we need |
36 |
| - * to update the system table and since updates are not allowed during |
37 |
| - * parallel mode we update the index statistics after exiting from the |
38 |
| - * parallel mode. |
| 19 | + * In practice VACUUM will often complete its initial pass over the target |
| 20 | + * heap relation without ever running out of space to store TIDs. This means |
| 21 | + * that there only needs to be one call to lazy_vacuum, after the initial pass |
| 22 | + * completes. |
39 | 23 | *
|
40 | 24 | * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
|
41 | 25 | * Portions Copyright (c) 1994, Regents of the University of California
|
|
124 | 108 | #define VACUUM_FSM_EVERY_PAGES \
|
125 | 109 | ((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) / BLCKSZ))
|
126 | 110 |
|
127 |
| -/* |
128 |
| - * Guesstimation of number of dead tuples per page. This is used to |
129 |
| - * provide an upper limit to memory allocated when vacuuming small |
130 |
| - * tables. |
131 |
| - */ |
132 |
| -#define LAZY_ALLOC_TUPLES MaxHeapTuplesPerPage |
133 |
| - |
134 | 111 | /*
|
135 | 112 | * Before we consider skipping a page that's marked as clean in
|
136 | 113 | * visibility map, we must've seen at least this many clean pages.
|
@@ -472,8 +449,9 @@ static void restore_vacuum_error_info(LVRelState *vacrel,
|
472 | 449 | /*
|
473 | 450 | * heap_vacuum_rel() -- perform VACUUM for one heap relation
|
474 | 451 | *
|
475 |
| - * This routine vacuums a single heap, cleans out its indexes, and |
476 |
| - * updates its relpages and reltuples statistics. |
| 452 | + * This routine sets things up for and then calls lazy_scan_heap, where |
| 453 | + * almost all work actually takes place. Finalizes everything after call |
| 454 | + * returns by managing rel truncation and updating pg_class statistics. |
477 | 455 | *
|
478 | 456 | * At entry, we have already established a transaction and opened
|
479 | 457 | * and locked the relation.
|
@@ -631,7 +609,10 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
|
631 | 609 | errcallback.previous = error_context_stack;
|
632 | 610 | error_context_stack = &errcallback;
|
633 | 611 |
|
634 |
| - /* Do the vacuuming */ |
| 612 | + /* |
| 613 | + * Call lazy_scan_heap to perform all required heap pruning, index |
| 614 | + * vacuuming, and heap vacuuming (plus related processing) |
| 615 | + */ |
635 | 616 | lazy_scan_heap(vacrel, params, aggressive);
|
636 | 617 |
|
637 | 618 | /* Done with indexes */
|
@@ -714,8 +695,8 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
|
714 | 695 | *
|
715 | 696 | * Deliberately avoid telling the stats collector about LP_DEAD items that
|
716 | 697 | * remain in the table due to VACUUM bypassing index and heap vacuuming.
|
717 |
| - * ANALYZE will consider the remaining LP_DEAD items to be dead tuples. It |
718 |
| - * seems like a good idea to err on the side of not vacuuming again too |
| 698 | + * ANALYZE will consider the remaining LP_DEAD items to be dead "tuples". |
| 699 | + * It seems like a good idea to err on the side of not vacuuming again too |
719 | 700 | * soon in cases where the failsafe prevented significant amounts of heap
|
720 | 701 | * vacuuming.
|
721 | 702 | */
|
@@ -875,20 +856,40 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
|
875 | 856 | }
|
876 | 857 |
|
877 | 858 | /*
|
878 |
| - * lazy_scan_heap() -- scan an open heap relation |
| 859 | + * lazy_scan_heap() -- workhorse function for VACUUM |
| 860 | + * |
| 861 | + * This routine prunes each page in the heap, and considers the need to |
| 862 | + * freeze remaining tuples with storage (not including pages that can be |
| 863 | + * skipped using the visibility map). Also performs related maintenance |
| 864 | + * of the FSM and visibility map. These steps all take place during an |
| 865 | + * initial pass over the target heap relation. |
| 866 | + * |
| 867 | + * Also invokes lazy_vacuum_all_indexes to vacuum indexes, which largely |
| 868 | + * consists of deleting index tuples that point to LP_DEAD items left in |
| 869 | + * heap pages following pruning. Earlier initial pass over the heap will |
| 870 | + * have collected the TIDs whose index tuples need to be removed. |
879 | 871 | *
|
880 |
| - * This routine prunes each page in the heap, which will among other |
881 |
| - * things truncate dead tuples to dead line pointers, defragment the |
882 |
| - * page, and set commit status bits (see heap_page_prune). It also builds |
883 |
| - * lists of dead tuples and pages with free space, calculates statistics |
884 |
| - * on the number of live tuples in the heap, and marks pages as |
885 |
| - * all-visible if appropriate. When done, or when we run low on space |
886 |
| - * for dead-tuple TIDs, invoke lazy_vacuum to vacuum indexes and vacuum |
887 |
| - * heap relation during its own second pass over the heap. |
| 872 | + * Finally, invokes lazy_vacuum_heap_rel to vacuum heap pages, which |
| 873 | + * largely consists of marking LP_DEAD items (from collected TID array) |
| 874 | + * as LP_UNUSED. This has to happen in a second, final pass over the |
| 875 | + * heap, to preserve a basic invariant that all index AMs rely on: no |
| 876 | + * extant index tuple can ever be allowed to contain a TID that points to |
| 877 | + * an LP_UNUSED line pointer in the heap. We must disallow premature |
| 878 | + * recycling of line pointers to avoid index scans that get confused |
| 879 | + * about which TID points to which tuple immediately after recycling. |
| 880 | + * (Actually, this isn't a concern when target heap relation happens to |
| 881 | + * have no indexes, which allows us to safely apply the one-pass strategy |
| 882 | + * as an optimization). |
888 | 883 | *
|
889 |
| - * If there are no indexes then we can reclaim line pointers on the fly; |
890 |
| - * dead line pointers need only be retained until all index pointers that |
891 |
| - * reference them have been killed. |
| 884 | + * In practice we often have enough space to fit all TIDs, and so won't |
| 885 | + * need to call lazy_vacuum more than once, after our initial pass over |
| 886 | + * the heap has totally finished. Otherwise things are slightly more |
| 887 | + * complicated: our "initial pass" over the heap applies only to those |
| 888 | + * pages that were pruned before we needed to call lazy_vacuum, and our |
| 889 | + * "final pass" over the heap only vacuums these same heap pages. |
| 890 | + * However, we process indexes in full every time lazy_vacuum is called, |
| 891 | + * which makes index processing very inefficient when memory is in short |
| 892 | + * supply. |
892 | 893 | */
|
893 | 894 | static void
|
894 | 895 | lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
|
@@ -1173,7 +1174,7 @@ lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
|
1173 | 1174 | vmbuffer = InvalidBuffer;
|
1174 | 1175 | }
|
1175 | 1176 |
|
1176 |
| - /* Remove the collected garbage tuples from table and indexes */ |
| 1177 | + /* Perform a round of index and heap vacuuming */ |
1177 | 1178 | vacrel->consider_bypass_optimization = false;
|
1178 | 1179 | lazy_vacuum(vacrel);
|
1179 | 1180 |
|
@@ -1490,12 +1491,12 @@ lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
|
1490 | 1491 | * visible to everyone yet actually are, and the PD_ALL_VISIBLE flag
|
1491 | 1492 | * is correct.
|
1492 | 1493 | *
|
1493 |
| - * There should never be dead tuples on a page with PD_ALL_VISIBLE |
| 1494 | + * There should never be LP_DEAD items on a page with PD_ALL_VISIBLE |
1494 | 1495 | * set, however.
|
1495 | 1496 | */
|
1496 | 1497 | else if (prunestate.has_lpdead_items && PageIsAllVisible(page))
|
1497 | 1498 | {
|
1498 |
| - elog(WARNING, "page containing dead tuples is marked as all-visible in relation \"%s\" page %u", |
| 1499 | + elog(WARNING, "page containing LP_DEAD items is marked as all-visible in relation \"%s\" page %u", |
1499 | 1500 | vacrel->relname, blkno);
|
1500 | 1501 | PageClearAllVisible(page);
|
1501 | 1502 | MarkBufferDirty(buf);
|
@@ -1585,7 +1586,7 @@ lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
|
1585 | 1586 | vmbuffer = InvalidBuffer;
|
1586 | 1587 | }
|
1587 | 1588 |
|
1588 |
| - /* If any tuples need to be deleted, perform final vacuum cycle */ |
| 1589 | + /* Perform a final round of index and heap vacuuming */ |
1589 | 1590 | if (dead_tuples->num_tuples > 0)
|
1590 | 1591 | lazy_vacuum(vacrel);
|
1591 | 1592 |
|
@@ -1816,13 +1817,14 @@ lazy_scan_prune(LVRelState *vacrel,
|
1816 | 1817 | * VACUUM can't run inside a transaction block, which makes some cases
|
1817 | 1818 | * impossible (e.g. in-progress insert from the same transaction).
|
1818 | 1819 | *
|
1819 |
| - * We treat LP_DEAD items a little differently, too -- we don't count |
1820 |
| - * them as dead_tuples at all (we only consider new_dead_tuples). The |
1821 |
| - * outcome is no different because we assume that any LP_DEAD items we |
1822 |
| - * encounter here will become LP_UNUSED inside lazy_vacuum_heap_page() |
1823 |
| - * before we report anything to the stats collector. (Cases where we |
1824 |
| - * bypass index vacuuming will violate our assumption, but the overall |
1825 |
| - * impact of that should be negligible.) |
| 1820 | + * We treat LP_DEAD items (which are the closest thing to DEAD tuples |
| 1821 | + * that might be seen here) differently, too: we assume that they'll |
| 1822 | + * become LP_UNUSED before VACUUM finishes. This difference is only |
| 1823 | + * superficial. VACUUM effectively agrees with ANALYZE about DEAD |
| 1824 | + * items, in the end. VACUUM won't remember LP_DEAD items, but only |
| 1825 | + * because they're not supposed to be left behind when it is done. |
| 1826 | + * (Cases where we bypass index vacuuming will violate this optimistic |
| 1827 | + * assumption, but the overall impact of that should be negligible.) |
1826 | 1828 | */
|
1827 | 1829 | switch (res)
|
1828 | 1830 | {
|
@@ -2169,7 +2171,7 @@ lazy_vacuum(LVRelState *vacrel)
|
2169 | 2171 | /*
|
2170 | 2172 | * Failsafe case.
|
2171 | 2173 | *
|
2172 |
| - * we attempted index vacuuming, but didn't finish a full round/full |
| 2174 | + * We attempted index vacuuming, but didn't finish a full round/full |
2173 | 2175 | * index scan. This happens when relfrozenxid or relminmxid is too
|
2174 | 2176 | * far in the past.
|
2175 | 2177 | *
|
@@ -3448,8 +3450,8 @@ compute_max_dead_tuples(BlockNumber relblocks, bool hasindex)
|
3448 | 3450 | maxtuples = Min(maxtuples, MAXDEADTUPLES(MaxAllocSize));
|
3449 | 3451 |
|
3450 | 3452 | /* curious coding here to ensure the multiplication can't overflow */
|
3451 |
| - if ((BlockNumber) (maxtuples / LAZY_ALLOC_TUPLES) > relblocks) |
3452 |
| - maxtuples = relblocks * LAZY_ALLOC_TUPLES; |
| 3453 | + if ((BlockNumber) (maxtuples / MaxHeapTuplesPerPage) > relblocks) |
| 3454 | + maxtuples = relblocks * MaxHeapTuplesPerPage; |
3453 | 3455 |
|
3454 | 3456 | /* stay sane if small maintenance_work_mem */
|
3455 | 3457 | maxtuples = Max(maxtuples, MaxHeapTuplesPerPage);
|
|
0 commit comments