Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e2ac3fe

Browse files
committed
Speed up rechecking if relation needs to be vacuumed or analyze in autovacuum.
After autovacuum collects the relations to vacuum or analyze, it rechecks whether each relation still needs to be vacuumed or analyzed before actually doing that. Previously this recheck could be a significant overhead especially when there were a very large number of relations. This was because each recheck forced the statistics to be refreshed, and the refresh of the statistics for a very large number of relations could cause heavy overhead. There was the report that this issue caused autovacuum workers to have gotten “stuck” in a tight loop of table_recheck_autovac() that rechecks whether a relation needs to be vacuumed or analyzed. This commit speeds up the recheck by making autovacuum worker reuse the previously-read statistics for the recheck if possible. Then if that "stale" statistics says that a relation still needs to be vacuumed or analyzed, autovacuum refreshes the statistics and does the recheck again. The benchmark shows that the more relations exist and autovacuum workers are running concurrently, the more this change reduces the autovacuum execution time. For example, when there are 20,000 tables and 10 autovacuum workers are running, the benchmark showed that the change improved the performance of autovacuum more than three times. On the other hand, even when there are only 1000 tables and only a single autovacuum worker is running, the benchmark didn't show any big performance regression by the change. Firstly POC patch was proposed by Jim Nasby. As the result of discussion, we used Tatsuhito Kasahara's version of the patch using the approach suggested by Tom Lane. Reported-by: Jim Nasby Author: Tatsuhito Kasahara Reviewed-by: Masahiko Sawada, Fujii Masao Discussion: https://postgr.es/m/3FC6C2F2-8A47-44C0-B997-28830B5716D0@amazon.com
1 parent 4e43ee8 commit e2ac3fe

File tree

1 file changed

+89
-19
lines changed

1 file changed

+89
-19
lines changed

src/backend/postmaster/autovacuum.c

+89-19
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,10 @@ static void FreeWorkerInfo(int code, Datum arg);
328328
static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
329329
TupleDesc pg_class_desc,
330330
int effective_multixact_freeze_max_age);
331+
static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts,
332+
Form_pg_class classForm,
333+
int effective_multixact_freeze_max_age,
334+
bool *dovacuum, bool *doanalyze, bool *wraparound);
331335
static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
332336
Form_pg_class classForm,
333337
PgStat_StatTabEntry *tabentry,
@@ -2797,17 +2801,9 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
27972801
bool dovacuum;
27982802
bool doanalyze;
27992803
autovac_table *tab = NULL;
2800-
PgStat_StatTabEntry *tabentry;
2801-
PgStat_StatDBEntry *shared;
2802-
PgStat_StatDBEntry *dbentry;
28032804
bool wraparound;
28042805
AutoVacOpts *avopts;
2805-
2806-
/* use fresh stats */
2807-
autovac_refresh_stats();
2808-
2809-
shared = pgstat_fetch_stat_dbentry(InvalidOid);
2810-
dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId);
2806+
static bool reuse_stats = false;
28112807

28122808
/* fetch the relation's relcache entry */
28132809
classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
@@ -2831,17 +2827,38 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
28312827
avopts = &hentry->ar_reloptions;
28322828
}
28332829

2834-
/* fetch the pgstat table entry */
2835-
tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
2836-
shared, dbentry);
2830+
/*
2831+
* Reuse the stats to recheck whether a relation needs to be vacuumed or
2832+
* analyzed if it was reloaded before and has not been cleared yet. This
2833+
* is necessary to avoid frequent refresh of stats, especially when there
2834+
* are very large number of relations and the refresh can cause lots of
2835+
* overhead.
2836+
*
2837+
* If we determined that a relation needs to be vacuumed or analyzed,
2838+
* based on the old stats, we refresh stats and recheck the necessity
2839+
* again. Because a relation may have already been vacuumed or analyzed by
2840+
* someone since the last reload of stats.
2841+
*/
2842+
if (reuse_stats)
2843+
{
2844+
recheck_relation_needs_vacanalyze(relid, avopts, classForm,
2845+
effective_multixact_freeze_max_age,
2846+
&dovacuum, &doanalyze, &wraparound);
28372847

2838-
relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
2839-
effective_multixact_freeze_max_age,
2840-
&dovacuum, &doanalyze, &wraparound);
2848+
/* Quick exit if a relation doesn't need to be vacuumed or analyzed */
2849+
if (!doanalyze && !dovacuum)
2850+
{
2851+
heap_freetuple(classTup);
2852+
return NULL;
2853+
}
2854+
}
28412855

2842-
/* ignore ANALYZE for toast tables */
2843-
if (classForm->relkind == RELKIND_TOASTVALUE)
2844-
doanalyze = false;
2856+
/* Use fresh stats and recheck again */
2857+
autovac_refresh_stats();
2858+
2859+
recheck_relation_needs_vacanalyze(relid, avopts, classForm,
2860+
effective_multixact_freeze_max_age,
2861+
&dovacuum, &doanalyze, &wraparound);
28452862

28462863
/* OK, it needs something done */
28472864
if (doanalyze || dovacuum)
@@ -2929,13 +2946,66 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
29292946
tab->at_dobalance =
29302947
!(avopts && (avopts->vacuum_cost_limit > 0 ||
29312948
avopts->vacuum_cost_delay > 0));
2949+
2950+
/*
2951+
* When we decide to do vacuum or analyze, the existing stats cannot
2952+
* be reused in the next cycle because it's cleared at the end of
2953+
* vacuum or analyze (by AtEOXact_PgStat()).
2954+
*/
2955+
reuse_stats = false;
2956+
}
2957+
else
2958+
{
2959+
/*
2960+
* If neither vacuum nor analyze is necessary, the existing stats is
2961+
* not cleared and can be reused in the next cycle.
2962+
*/
2963+
reuse_stats = true;
29322964
}
29332965

29342966
heap_freetuple(classTup);
2935-
29362967
return tab;
29372968
}
29382969

2970+
/*
2971+
* recheck_relation_needs_vacanalyze
2972+
*
2973+
* Subroutine for table_recheck_autovac.
2974+
*
2975+
* Fetch the pgstat of a relation and recheck whether a relation
2976+
* needs to be vacuumed or analyzed.
2977+
*/
2978+
static void
2979+
recheck_relation_needs_vacanalyze(Oid relid,
2980+
AutoVacOpts *avopts,
2981+
Form_pg_class classForm,
2982+
int effective_multixact_freeze_max_age,
2983+
bool *dovacuum,
2984+
bool *doanalyze,
2985+
bool *wraparound)
2986+
{
2987+
PgStat_StatTabEntry *tabentry;
2988+
PgStat_StatDBEntry *shared = NULL;
2989+
PgStat_StatDBEntry *dbentry = NULL;
2990+
2991+
if (classForm->relisshared)
2992+
shared = pgstat_fetch_stat_dbentry(InvalidOid);
2993+
else
2994+
dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId);
2995+
2996+
/* fetch the pgstat table entry */
2997+
tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
2998+
shared, dbentry);
2999+
3000+
relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
3001+
effective_multixact_freeze_max_age,
3002+
dovacuum, doanalyze, wraparound);
3003+
3004+
/* ignore ANALYZE for toast tables */
3005+
if (classForm->relkind == RELKIND_TOASTVALUE)
3006+
*doanalyze = false;
3007+
}
3008+
29393009
/*
29403010
* relation_needs_vacanalyze
29413011
*

0 commit comments

Comments
 (0)