Skip to content

Commit ee78ad5

Browse files
committed
Avoid having vacuum set reltuples to 0 on non-empty relations in the
presence of page pins, which leads to serious estimation errors in the planner. This particularly affects small heavily-accessed tables, especially where locking (e.g. from FK constraints) forces frequent vacuums for mxid cleanup. Fix by keeping separate track of pages whose live tuples were actually counted vs. pages that were only scanned for freezing purposes. Thus, reltuples can only be set to 0 if all pages of the relation were actually counted. Backpatch to all supported versions. Per bug #14057 from Nicolas Baccelli, analyzed by me. Discussion: https://postgr.es/m/20160331103739.8956.94469@wrigleys.postgresql.org
1 parent 087e696 commit ee78ad5

File tree

1 file changed

+11
-4
lines changed

1 file changed

+11
-4
lines changed

src/backend/commands/vacuumlazy.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ typedef struct LVRelStats
106106
BlockNumber rel_pages; /* total number of pages */
107107
BlockNumber scanned_pages; /* number of pages we examined */
108108
BlockNumber pinskipped_pages; /* # of pages we skipped due to a pin */
109-
double scanned_tuples; /* counts only tuples on scanned pages */
109+
BlockNumber tupcount_pages; /* pages whose tuples we counted */
110+
double scanned_tuples; /* counts only tuples on tupcount_pages */
110111
double old_rel_tuples; /* previous value of pg_class.reltuples */
111112
double new_rel_tuples; /* new estimated total # of tuples */
112113
double new_dead_tuples; /* new estimated total # of dead tuples */
@@ -286,6 +287,10 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
286287
* density") with nonzero relpages and reltuples=0 (which means "zero
287288
* tuple density") unless there's some actual evidence for the latter.
288289
*
290+
* It's important that we use tupcount_pages and not scanned_pages for the
291+
* check described above; scanned_pages counts pages where we could not get
292+
* cleanup lock, and which were processed only for frozenxid purposes.
293+
*
289294
* We do update relallvisible even in the corner case, since if the table
290295
* is all-visible we'd definitely like to know that. But clamp the value
291296
* to be not more than what we're setting relpages to.
@@ -295,7 +300,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
295300
*/
296301
new_rel_pages = vacrelstats->rel_pages;
297302
new_rel_tuples = vacrelstats->new_rel_tuples;
298-
if (vacrelstats->scanned_pages == 0 && new_rel_pages > 0)
303+
if (vacrelstats->tupcount_pages == 0 && new_rel_pages > 0)
299304
{
300305
new_rel_pages = vacrelstats->old_rel_pages;
301306
new_rel_tuples = vacrelstats->old_rel_tuples;
@@ -474,6 +479,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
474479
nblocks = RelationGetNumberOfBlocks(onerel);
475480
vacrelstats->rel_pages = nblocks;
476481
vacrelstats->scanned_pages = 0;
482+
vacrelstats->tupcount_pages = 0;
477483
vacrelstats->nonempty_pages = 0;
478484
vacrelstats->latestRemovedXid = InvalidTransactionId;
479485

@@ -666,6 +672,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
666672
}
667673

668674
vacrelstats->scanned_pages++;
675+
vacrelstats->tupcount_pages++;
669676

670677
page = BufferGetPage(buf);
671678

@@ -1074,7 +1081,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
10741081
/* now we can compute the new value for pg_class.reltuples */
10751082
vacrelstats->new_rel_tuples = vac_estimate_reltuples(onerel, false,
10761083
nblocks,
1077-
vacrelstats->scanned_pages,
1084+
vacrelstats->tupcount_pages,
10781085
num_tuples);
10791086

10801087
/*
@@ -1393,7 +1400,7 @@ lazy_cleanup_index(Relation indrel,
13931400

13941401
ivinfo.index = indrel;
13951402
ivinfo.analyze_only = false;
1396-
ivinfo.estimated_count = (vacrelstats->scanned_pages < vacrelstats->rel_pages);
1403+
ivinfo.estimated_count = (vacrelstats->tupcount_pages < vacrelstats->rel_pages);
13971404
ivinfo.message_level = elevel;
13981405
ivinfo.num_heap_tuples = vacrelstats->new_rel_tuples;
13991406
ivinfo.strategy = vac_strategy;

0 commit comments

Comments
 (0)