2 * Copyright (c) 2011 Teodor Sigaev <teodor@sigaev.ru>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of any co-contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include "catalog/namespace.h"
34 #include "commands/vacuum.h"
35 #include "executor/executor.h"
36 #include "nodes/nodes.h"
37 #include "nodes/parsenodes.h"
38 #include "storage/bufmgr.h"
39 #include "utils/lsyscache.h"
40 #include "utils/guc.h"
42 #ifdef PG_MODULE_MAGIC
46 static bool online_analyze_enable = true;
47 static bool online_analyze_verbose = true;
48 static double online_analyze_scale_factor = 0.1;
49 static int online_analyze_threshold = 50;
50 static double online_analyze_min_interval = 10000;
52 static ExecutorEnd_hook_type oldhook = NULL;
55 makeAnalyze(Oid relOid, CmdType operation, uint32 naffected)
57 PgStat_StatTabEntry *tabentry;
58 TimestampTz now = GetCurrentTimestamp();
60 if (relOid == InvalidOid)
63 tabentry = pgstat_fetch_stat_tabentry(relOid);
65 #if PG_VERSION_NUM >= 90000
66 #define changes_since_analyze(t) ((t)->changes_since_analyze)
68 #define changes_since_analyze(t) ((t)->n_live_tuples + (t)->n_dead_tuples - (t)->last_anl_tuples)
72 tabentry == NULL /* a new table */ ||
74 /* do not analyze too often, if both stamps are exceeded the go */
75 TimestampDifferenceExceeds(tabentry->analyze_timestamp, now, online_analyze_min_interval) &&
76 TimestampDifferenceExceeds(tabentry->autovac_analyze_timestamp, now, online_analyze_min_interval) &&
77 /* be in sync with relation_needs_vacanalyze */
78 ((double)(changes_since_analyze(tabentry) + naffected)) >=
79 online_analyze_scale_factor * ((double)(tabentry->n_dead_tuples + tabentry->n_live_tuples)) +
80 (double)online_analyze_threshold
85 TimestampTz startStamp, endStamp;
87 vacstmt.type = T_VacuumStmt;
88 vacstmt.freeze_min_age = -1;
89 vacstmt.freeze_table_age = -1; /* ??? */
90 vacstmt.relation = NULL;
91 vacstmt.va_cols = NIL;
93 #if PG_VERSION_NUM >= 90000
94 vacstmt.options = VACOPT_ANALYZE;
95 if (online_analyze_verbose)
96 vacstmt.options |= VACOPT_VERBOSE;
98 vacstmt.vacuum = vacstmt.full = false;
99 vacstmt.analyze = true;
100 vacstmt.verbose = online_analyze_verbose;
103 if (online_analyze_verbose)
104 startStamp = GetCurrentTimestamp();
106 analyze_rel(relOid, &vacstmt, GetAccessStrategy(BAS_VACUUM)
107 #if (PG_VERSION_NUM < 90004) && (PG_VERSION_NUM >= 90000)
112 if (online_analyze_verbose)
117 endStamp = GetCurrentTimestamp();
118 TimestampDifference(startStamp, endStamp, &secs, µsecs);
119 elog(INFO, "analyze \"%s\" took %.02f seconds", get_rel_name(relOid), ((double)secs) + ((double)microsecs)/1.0e6);
123 if (tabentry == NULL)
126 pgstat_clear_snapshot();
130 /* update last analyze timestamp in local memory of backend */
131 tabentry->analyze_timestamp = now;
134 #if PG_VERSION_NUM >= 90000
135 else if (tabentry != NULL)
137 tabentry->changes_since_analyze += naffected;
142 extern PGDLLIMPORT void onlineAnalyzeHooker(QueryDesc *queryDesc);
144 onlineAnalyzeHooker(QueryDesc *queryDesc)
146 uint32 naffected = 0;
148 if (queryDesc->estate)
149 naffected = queryDesc->estate->es_processed;
151 if (online_analyze_enable && queryDesc->plannedstmt &&
152 (queryDesc->operation == CMD_INSERT ||
153 queryDesc->operation == CMD_UPDATE ||
154 queryDesc->operation == CMD_DELETE ||
155 (queryDesc->operation == CMD_SELECT && queryDesc->plannedstmt->intoClause)))
157 if (queryDesc->plannedstmt->intoClause)
159 Oid relOid = RangeVarGetRelid(queryDesc->plannedstmt->intoClause->rel, true);
161 makeAnalyze(relOid, queryDesc->operation, naffected);
163 else if (queryDesc->plannedstmt->resultRelations &&
164 queryDesc->plannedstmt->rtable)
168 foreach(l, queryDesc->plannedstmt->resultRelations)
170 int n = lfirst_int(l);
171 RangeTblEntry *rte = list_nth(queryDesc->plannedstmt->rtable, n-1);
173 if (rte->rtekind == RTE_RELATION)
174 makeAnalyze(rte->relid, queryDesc->operation, naffected);
180 (*oldhook)(queryDesc);
182 standard_ExecutorEnd(queryDesc);
189 oldhook = ExecutorEnd_hook;
191 ExecutorEnd_hook = onlineAnalyzeHooker;
193 DefineCustomBoolVariable(
194 "online_analyze.enable",
195 "Enable on-line analyze",
196 "Enables analyze of table directly after insert/update/delete/select into",
197 &online_analyze_enable,
198 #if PG_VERSION_NUM >= 80400
199 online_analyze_enable,
202 #if PG_VERSION_NUM >= 80400
204 #if PG_VERSION_NUM >= 90100
212 DefineCustomBoolVariable(
213 "online_analyze.verbose",
214 "Verbosity of on-line analyze",
215 "Make ANALYZE VERBOSE after table's changes",
216 &online_analyze_verbose,
217 #if PG_VERSION_NUM >= 80400
218 online_analyze_verbose,
221 #if PG_VERSION_NUM >= 80400
223 #if PG_VERSION_NUM >= 90100
231 DefineCustomRealVariable(
232 "online_analyze.scale_factor",
233 "fraction of table size to start on-line analyze",
234 "fraction of table size to start on-line analyze",
235 &online_analyze_scale_factor,
236 #if PG_VERSION_NUM >= 80400
237 online_analyze_scale_factor,
242 #if PG_VERSION_NUM >= 80400
244 #if PG_VERSION_NUM >= 90100
252 DefineCustomIntVariable(
253 "online_analyze.threshold",
254 "min number of row updates before on-line analyze",
255 "min number of row updates before on-line analyze",
256 &online_analyze_threshold,
257 #if PG_VERSION_NUM >= 80400
258 online_analyze_threshold,
263 #if PG_VERSION_NUM >= 80400
265 #if PG_VERSION_NUM >= 90100
273 DefineCustomRealVariable(
274 "online_analyze.min_interval",
275 "minimum time interval between analyze call (in milliseconds)",
276 "minimum time interval between analyze call (in milliseconds)",
277 &online_analyze_scale_factor,
278 #if PG_VERSION_NUM >= 80400
279 online_analyze_min_interval,
284 #if PG_VERSION_NUM >= 80400
286 #if PG_VERSION_NUM >= 90100
300 ExecutorEnd_hook = oldhook;