From: teodor Date: Thu, 29 Sep 2011 12:26:25 +0000 (+0000) Subject: Initial revision X-Git-Tag: start~1 X-Git-Url: http://sigaev.ru/git/gitweb.cgi?a=commitdiff_plain;h=f0c6e046c6dca0fc8258472e692800e1ba94530b;p=online_analyze.git Initial revision --- f0c6e046c6dca0fc8258472e692800e1ba94530b diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..333add2 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +MODULE_big = online_analyze +OBJS = online_analyze.o +#DATA_built = online_analyze.sql +DOCS = README.online_analyze +#REGRESS = online_analyze + +ifdef USE_PGXS +PGXS := $(shell pg_config --pgxs) +include $(PGXS) +else +subdir = contrib/online_analyze +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + diff --git a/README.online_analyze b/README.online_analyze new file mode 100644 index 0000000..edf5059 --- /dev/null +++ b/README.online_analyze @@ -0,0 +1,26 @@ +Module makes an analyze call immediately after INSERT/UPDATE/DELETE/SELECT INTO +for affected table(s). + +Supported versionsi of PostgreSQL: 8.4.*, 9.0.*, 9.1.* + +Usage: LOAD 'online_analyze'; + +Custom variables (defaults values are shown): +online_analyze.enable = on + Enables on-line analyze + +online_analyze.verbose = on + Execute ANALYZE VERBOSE + +online_analyze.scale_factor = 0.1 + Fraction of table size to start on-line analyze (similar to + autovacuum_analyze_scale_factor) + +online_analyze.threshold = 50 + Min number of row updates before on-line analyze (similar to + autovacuum_analyze_threshold) + +online_analyze.min_interval = 10000 + Minimum time interval between analyze call per table (in milliseconds) + +Author: Teodor Sigaev diff --git a/online_analyze.c b/online_analyze.c new file mode 100644 index 0000000..d8010af --- /dev/null +++ b/online_analyze.c @@ -0,0 +1,272 @@ +#include "postgres.h" + +#include "pgstat.h" +#include "catalog/namespace.h" +#include "commands/vacuum.h" +#include "executor/executor.h" +#include "nodes/nodes.h" +#include "nodes/parsenodes.h" +#include "storage/bufmgr.h" +#include "utils/lsyscache.h" +#include "utils/guc.h" + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif + +static bool online_analyze_enable = true; +static bool online_analyze_verbose = true; +static double online_analyze_scale_factor = 0.1; +static int online_analyze_threshold = 50; +static double online_analyze_min_interval = 10000; + +static ExecutorEnd_hook_type oldhook = NULL; + +static void +makeAnalyze(Oid relOid, CmdType operation, uint32 naffected) +{ + PgStat_StatTabEntry *tabentry; + TimestampTz now = GetCurrentTimestamp(); + + if (relOid == InvalidOid) + return; + + tabentry = pgstat_fetch_stat_tabentry(relOid); + +#if PG_VERSION_NUM >= 90000 +#define changes_since_analyze(t) ((t)->changes_since_analyze) +#else +#define changes_since_analyze(t) ((t)->n_live_tuples + (t)->n_dead_tuples - (t)->last_anl_tuples) +#endif + + if ( + tabentry == NULL /* a new table */ || + ( + /* do not analyze too often, if both stamps are exceeded the go */ + TimestampDifferenceExceeds(tabentry->analyze_timestamp, now, online_analyze_min_interval) && + TimestampDifferenceExceeds(tabentry->autovac_analyze_timestamp, now, online_analyze_min_interval) && + /* be in sync with relation_needs_vacanalyze */ + ((double)(changes_since_analyze(tabentry) + naffected)) >= + online_analyze_scale_factor * ((double)(tabentry->n_dead_tuples + tabentry->n_live_tuples)) + + (double)online_analyze_threshold + ) + ) + { + VacuumStmt vacstmt; + TimestampTz startStamp, endStamp; + + vacstmt.type = T_VacuumStmt; + vacstmt.freeze_min_age = -1; + vacstmt.freeze_table_age = -1; /* ??? */ + vacstmt.relation = NULL; + vacstmt.va_cols = NIL; + +#if PG_VERSION_NUM >= 90000 + vacstmt.options = VACOPT_ANALYZE; + if (online_analyze_verbose) + vacstmt.options |= VACOPT_VERBOSE; +#else + vacstmt.vacuum = vacstmt.full = false; + vacstmt.analyze = true; + vacstmt.verbose = online_analyze_verbose; +#endif + + if (online_analyze_verbose) + startStamp = GetCurrentTimestamp(); + + analyze_rel(relOid, &vacstmt, GetAccessStrategy(BAS_VACUUM) +#if (PG_VERSION_NUM < 90004) && (PG_VERSION_NUM >= 90000) + , true +#endif + ); + + if (online_analyze_verbose) + { + long secs; + int microsecs; + + endStamp = GetCurrentTimestamp(); + TimestampDifference(startStamp, endStamp, &secs, µsecs); + elog(INFO, "analyze \"%s\" took %.02f seconds", get_rel_name(relOid), ((double)secs) + ((double)microsecs)/1.0e6); + } + + + if (tabentry == NULL) + { + /* new table */ + pgstat_clear_snapshot(); + } + else + { + /* update last analyze timestamp in local memory of backend */ + tabentry->analyze_timestamp = now; + } + } +#if PG_VERSION_NUM >= 90000 + else if (tabentry != NULL) + { + tabentry->changes_since_analyze += naffected; + } +#endif +} + +extern PGDLLIMPORT void onlineAnalyzeHooker(QueryDesc *queryDesc); +void +onlineAnalyzeHooker(QueryDesc *queryDesc) +{ + uint32 naffected = 0; + + if (queryDesc->estate) + naffected = queryDesc->estate->es_processed; + + if (online_analyze_enable && queryDesc->plannedstmt && + (queryDesc->operation == CMD_INSERT || + queryDesc->operation == CMD_UPDATE || + queryDesc->operation == CMD_DELETE || + (queryDesc->operation == CMD_SELECT && queryDesc->plannedstmt->intoClause))) + { + if (queryDesc->plannedstmt->intoClause) + { + Oid relOid = RangeVarGetRelid(queryDesc->plannedstmt->intoClause->rel, true); + + makeAnalyze(relOid, queryDesc->operation, naffected); + } + else if (queryDesc->plannedstmt->resultRelations && + queryDesc->plannedstmt->rtable) + { + ListCell *l; + + foreach(l, queryDesc->plannedstmt->resultRelations) + { + int n = lfirst_int(l); + RangeTblEntry *rte = list_nth(queryDesc->plannedstmt->rtable, n-1); + + if (rte->rtekind == RTE_RELATION) + makeAnalyze(rte->relid, queryDesc->operation, naffected); + } + } + } + + if (oldhook) + (*oldhook)(queryDesc); + else + standard_ExecutorEnd(queryDesc); +} + +void _PG_init(void); +void +_PG_init(void) +{ + oldhook = ExecutorEnd_hook; + + ExecutorEnd_hook = onlineAnalyzeHooker; + + DefineCustomBoolVariable( + "online_analyze.enable", + "Enable on-line analyze", + "Enables analyze of table directly after insert/update/delete/select into", + &online_analyze_enable, +#if PG_VERSION_NUM >= 80400 + online_analyze_enable, +#endif + PGC_USERSET, +#if PG_VERSION_NUM >= 80400 + GUC_NOT_IN_SAMPLE, +#if PG_VERSION_NUM >= 90100 + NULL, +#endif +#endif + NULL, + NULL + ); + + DefineCustomBoolVariable( + "online_analyze.verbose", + "Verbosity of on-line analyze", + "Make ANALYZE VERBOSE after table's changes", + &online_analyze_verbose, +#if PG_VERSION_NUM >= 80400 + online_analyze_verbose, +#endif + PGC_USERSET, +#if PG_VERSION_NUM >= 80400 + GUC_NOT_IN_SAMPLE, +#if PG_VERSION_NUM >= 90100 + NULL, +#endif +#endif + NULL, + NULL + ); + + DefineCustomRealVariable( + "online_analyze.scale_factor", + "fraction of table size to start on-line analyze", + "fraction of table size to start on-line analyze", + &online_analyze_scale_factor, +#if PG_VERSION_NUM >= 80400 + online_analyze_scale_factor, +#endif + 0.0, + 1.0, + PGC_USERSET, +#if PG_VERSION_NUM >= 80400 + GUC_NOT_IN_SAMPLE, +#if PG_VERSION_NUM >= 90100 + NULL, +#endif +#endif + NULL, + NULL + ); + + DefineCustomIntVariable( + "online_analyze.threshold", + "min number of row updates before on-line analyze", + "min number of row updates before on-line analyze", + &online_analyze_threshold, +#if PG_VERSION_NUM >= 80400 + online_analyze_threshold, +#endif + 0, + 0x7fffffff, + PGC_USERSET, +#if PG_VERSION_NUM >= 80400 + GUC_NOT_IN_SAMPLE, +#if PG_VERSION_NUM >= 90100 + NULL, +#endif +#endif + NULL, + NULL + ); + + DefineCustomRealVariable( + "online_analyze.min_interval", + "minimum time interval between analyze call (in milliseconds)", + "minimum time interval between analyze call (in milliseconds)", + &online_analyze_scale_factor, +#if PG_VERSION_NUM >= 80400 + online_analyze_min_interval, +#endif + 0.0, + 1e30, + PGC_USERSET, +#if PG_VERSION_NUM >= 80400 + GUC_NOT_IN_SAMPLE, +#if PG_VERSION_NUM >= 90100 + NULL, +#endif +#endif + NULL, + NULL + ); + +} + +void _PG_fini(void); +void +_PG_fini(void) +{ + ExecutorEnd_hook = oldhook; +}