|
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