From 380e706f1c3c7b7faee28cf21cdad262d4779cfa Mon Sep 17 00:00:00 2001 From: djshow832 <873581766@qq.com> Date: Thu, 21 Nov 2019 23:12:38 +0800 Subject: [PATCH 1/7] refresh statement summary --- domain/global_vars_cache.go | 6 + executor/set.go | 7 +- infoschema/perfschema/const.go | 1 + infoschema/perfschema/tables_test.go | 31 +++--- session/session.go | 1 + sessionctx/variable/sysvar.go | 1 + sessionctx/variable/tidb_vars.go | 4 + sessionctx/variable/varsutil.go | 12 ++ sessionctx/variable/varsutil_test.go | 16 +++ util/stmtsummary/statement_summary.go | 123 +++++++++++++++++---- util/stmtsummary/statement_summary_test.go | 61 +++++++++- 11 files changed, 223 insertions(+), 40 deletions(-) diff --git a/domain/global_vars_cache.go b/domain/global_vars_cache.go index 0c7aba1176d00..979a3023f7646 100644 --- a/domain/global_vars_cache.go +++ b/domain/global_vars_cache.go @@ -76,6 +76,12 @@ func checkEnableServerGlobalVar(rows []chunk.Row) { sVal = row.GetString(1) } stmtsummary.StmtSummaryByDigestMap.SetEnabled(sVal, false) + case variable.TiDBStmtSummaryRefreshInterval: + sVal := "" + if !row.IsNull(1) { + sVal = row.GetString(1) + } + stmtsummary.StmtSummaryByDigestMap.SetRefreshInterval(sVal, false) case variable.TiDBCapturePlanBaseline: sVal := "" if !row.IsNull(1) { diff --git a/executor/set.go b/executor/set.go index a3f37e6d5819e..3dda3e8bbe0c1 100644 --- a/executor/set.go +++ b/executor/set.go @@ -197,9 +197,12 @@ func (e *SetExecutor) setSysVariable(name string, v *expression.VarAssignment) e } } - if name == variable.TiDBEnableStmtSummary { + switch name { + case variable.TiDBEnableStmtSummary: stmtsummary.StmtSummaryByDigestMap.SetEnabled(valStr, !v.IsGlobal) - } else if name == variable.TiDBCapturePlanBaseline { + case variable.TiDBStmtSummaryRefreshInterval: + stmtsummary.StmtSummaryByDigestMap.SetRefreshInterval(valStr, !v.IsGlobal) + case variable.TiDBCapturePlanBaseline: variable.CapturePlanBaseline.Set(valStr, !v.IsGlobal) } diff --git a/infoschema/perfschema/const.go b/infoschema/perfschema/const.go index 30d09779d9cdd..6938e9ee31c6e 100644 --- a/infoschema/perfschema/const.go +++ b/infoschema/perfschema/const.go @@ -385,6 +385,7 @@ const tableStagesHistoryLong = "CREATE TABLE if not exists performance_schema.ev // tableEventsStatementsSummaryByDigest contains the column name definitions for table // events_statements_summary_by_digest, same as MySQL. const tableEventsStatementsSummaryByDigest = "CREATE TABLE if not exists events_statements_summary_by_digest (" + + "SUMMARY_BEGIN_TIME TIMESTAMP(6) NOT NULL," + "STMT_TYPE VARCHAR(64) NOT NULL," + "SCHEMA_NAME VARCHAR(64) DEFAULT NULL," + "DIGEST VARCHAR(64) NOT NULL," + diff --git a/infoschema/perfschema/tables_test.go b/infoschema/perfschema/tables_test.go index 8fbd0a416bc9a..0827e72b99022 100644 --- a/infoschema/perfschema/tables_test.go +++ b/infoschema/perfschema/tables_test.go @@ -64,14 +64,14 @@ func (s *testTableSuite) TestPerfSchemaTables(c *C) { tk.MustQuery("select * from events_stages_history_long").Check(testkit.Rows()) } -// Test events_statements_summary_by_digest +// Test events_statements_summary_by_digest. func (s *testTableSuite) TestStmtSummaryTable(c *C) { tk := testkit.NewTestKitWithInit(c, s.store) tk.MustExec("drop table if exists t") tk.MustExec("create table t(a int, b varchar(10), key k(a))") - // Statement summary is disabled by default + // Statement summary is disabled by default. tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("0")) tk.MustExec("insert into t values(1, 'a')") tk.MustQuery("select * from performance_schema.events_statements_summary_by_digest").Check(testkit.Rows()) @@ -81,8 +81,11 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) { // Invalidate the cache manually so that tidb_enable_stmt_summary works immediately. s.dom.GetGlobalVarsCache().Disable() + // Disable refreshing summary. + tk.MustExec("set global tidb_stmt_summary_refresh_interval = 9999999999") + tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("9999999999")) - // Create a new session to test + // Create a new session to test. tk = testkit.NewTestKitWithInit(c, s.store) // Test INSERT @@ -97,7 +100,7 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) { where digest_text like 'insert into t%'`, ).Check(testkit.Rows("insert test test.t 4 0 0 0 0 0 2 2 1 1 1 /**/insert into t values(4, 'd')")) - // Test SELECT + // Test SELECT. tk.MustQuery("select * from t where a=2") tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys, max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, @@ -114,26 +117,26 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) { order by exec_count desc limit 1`, ).Check(testkit.Rows("insert test test.t 4 0 0 0 0 0 2 2 1 1 1 /**/insert into t values(4, 'd')")) - // Disable it again + // Disable it again. tk.MustExec("set global tidb_enable_stmt_summary = false") tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("0")) // Create a new session to test tk = testkit.NewTestKitWithInit(c, s.store) - // This statement shouldn't be summarized + // This statement shouldn't be summarized. tk.MustQuery("select * from t where a=2") - // The table should be cleared + // The table should be cleared. tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys, max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, max_prewrite_regions, avg_affected_rows, query_sample_text from performance_schema.events_statements_summary_by_digest`, ).Check(testkit.Rows()) - // Enable it in session scope + // Enable it in session scope. tk.MustExec("set session tidb_enable_stmt_summary = on") - // It should work immediately + // It should work immediately. tk.MustExec("begin") tk.MustExec("insert into t values(1, 'a')") tk.MustExec("commit") @@ -158,15 +161,15 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) { where digest_text like 'select * from t%'`, ).Check(testkit.Rows("select test test.t t:k 1 2 0 0 0 0 0 0 0 0 0 select * from t where a=2")) - // Disable it in global scope + // Disable it in global scope. tk.MustExec("set global tidb_enable_stmt_summary = off") - // Create a new session to test + // Create a new session to test. tk = testkit.NewTestKitWithInit(c, s.store) tk.MustQuery("select * from t where a=2") - // Statement summary is still enabled + // Statement summary is still enabled. tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys, max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, max_prewrite_regions, avg_affected_rows, query_sample_text @@ -174,11 +177,11 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) { where digest_text like 'select * from t%'`, ).Check(testkit.Rows("select test test.t t:k 2 4 0 0 0 0 0 0 0 0 0 select * from t where a=2")) - // Unset session variable + // Unset session variable. tk.MustExec("set session tidb_enable_stmt_summary = ''") tk.MustQuery("select * from t where a=2") - // Statement summary is disabled + // Statement summary is disabled. tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys, max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, max_prewrite_regions, avg_affected_rows, query_sample_text diff --git a/session/session.go b/session/session.go index cdac92bf8a989..4c144fe260864 100644 --- a/session/session.go +++ b/session/session.go @@ -1851,6 +1851,7 @@ var builtinGlobalVariable = []string{ variable.TiDBEnableIndexMerge, variable.TiDBTxnMode, variable.TiDBEnableStmtSummary, + variable.TiDBStmtSummaryRefreshInterval, variable.TiDBMaxDeltaSchemaCount, variable.TiDBCapturePlanBaseline, variable.TiDBUsePlanBaselines, diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index 0aa73d83148ac..22b042941948e 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -717,6 +717,7 @@ var defaultSysVars = []*SysVar{ {ScopeSession, TiDBReplicaRead, "leader"}, {ScopeSession, TiDBAllowRemoveAutoInc, BoolToIntStr(DefTiDBAllowRemoveAutoInc)}, {ScopeGlobal | ScopeSession, TiDBEnableStmtSummary, "0"}, + {ScopeGlobal | ScopeSession, TiDBStmtSummaryRefreshInterval, strconv.Itoa(DefTiDBStmtSummaryRefreshInterval)}, {ScopeGlobal | ScopeSession, TiDBCapturePlanBaseline, "0"}, {ScopeGlobal | ScopeSession, TiDBUsePlanBaselines, BoolToIntStr(DefTiDBUsePlanBaselines)}, {ScopeGlobal | ScopeSession, TiDBEvolvePlanBaselines, BoolToIntStr(DefTiDBEvolvePlanBaselines)}, diff --git a/sessionctx/variable/tidb_vars.go b/sessionctx/variable/tidb_vars.go index 3e8fec53ff939..a316a6e5775ef 100644 --- a/sessionctx/variable/tidb_vars.go +++ b/sessionctx/variable/tidb_vars.go @@ -336,6 +336,9 @@ const ( // TiDBEnableStmtSummary indicates whether the statement summary is enabled. TiDBEnableStmtSummary = "tidb_enable_stmt_summary" + // TiDBStmtSummaryRefreshInterval indicates the refresh interval in seconds for each statement summary. + TiDBStmtSummaryRefreshInterval = "tidb_stmt_summary_refresh_interval" + // TiDBCapturePlanBaseline indicates whether the capture of plan baselines is enabled. TiDBCapturePlanBaseline = "tidb_capture_plan_baselines" @@ -426,6 +429,7 @@ const ( DefWaitSplitRegionTimeout = 300 // 300s DefTiDBEnableNoopFuncs = false DefTiDBAllowRemoveAutoInc = false + DefTiDBStmtSummaryRefreshInterval = 1800 DefTiDBUsePlanBaselines = true DefTiDBEvolvePlanBaselines = false DefTiDBEvolvePlanTaskMaxTime = 600 // 600s diff --git a/sessionctx/variable/varsutil.go b/sessionctx/variable/varsutil.go index 6f3df1a8ba0d2..5148dd5338c97 100644 --- a/sessionctx/variable/varsutil.go +++ b/sessionctx/variable/varsutil.go @@ -629,6 +629,18 @@ func ValidateSetSystemVar(vars *SessionVars, name string, value string) (string, return "", nil } return value, ErrWrongValueForVar.GenWithStackByArgs(name, value) + case TiDBStmtSummaryRefreshInterval: + if value == "" { + return "", nil + } + v, err := strconv.Atoi(value) + if err != nil { + return value, ErrWrongTypeForVar.GenWithStackByArgs(name) + } + if v <= 0 { + return value, ErrWrongValueForVar.GenWithStackByArgs(name, value) + } + return value, nil case TiDBIsolationReadEngines: engines := strings.Split(value, ",") var formatVal string diff --git a/sessionctx/variable/varsutil_test.go b/sessionctx/variable/varsutil_test.go index 87a9aadb2e3f0..0e305bbe18abe 100644 --- a/sessionctx/variable/varsutil_test.go +++ b/sessionctx/variable/varsutil_test.go @@ -370,6 +370,16 @@ func (s *testVarsutilSuite) TestVarsutil(c *C) { c.Assert(err, IsNil) c.Assert(val, Equals, "leader") c.Assert(v.GetReplicaRead(), Equals, kv.ReplicaReadLeader) + + SetSessionSystemVar(v, TiDBEnableStmtSummary, types.NewStringDatum("on")) + val, err = GetSessionSystemVar(v, TiDBEnableStmtSummary) + c.Assert(err, IsNil) + c.Assert(val, Equals, "1") + + SetSessionSystemVar(v, TiDBStmtSummaryRefreshInterval, types.NewStringDatum("10")) + val, err = GetSessionSystemVar(v, TiDBStmtSummaryRefreshInterval) + c.Assert(err, IsNil) + c.Assert(val, Equals, "10") } func (s *testVarsutilSuite) TestSetOverflowBehave(c *C) { @@ -463,6 +473,12 @@ func (s *testVarsutilSuite) TestValidate(c *C) { {TiDBIsolationReadEngines, "tikv", false}, {TiDBIsolationReadEngines, "TiKV,tiflash", false}, {TiDBIsolationReadEngines, " tikv, tiflash ", false}, + {TiDBEnableStmtSummary, "a", true}, + {TiDBEnableStmtSummary, "-1", true}, + {TiDBEnableStmtSummary, "", false}, + {TiDBStmtSummaryRefreshInterval, "0", true}, + {TiDBStmtSummaryRefreshInterval, "a", true}, + {TiDBStmtSummaryRefreshInterval, "", false}, } for _, t := range tests { diff --git a/util/stmtsummary/statement_summary.go b/util/stmtsummary/statement_summary.go index d8bcf81ab1844..30b4f90b91887 100644 --- a/util/stmtsummary/statement_summary.go +++ b/util/stmtsummary/statement_summary.go @@ -17,6 +17,7 @@ import ( "bytes" "fmt" "sort" + "strconv" "strings" "sync" "sync/atomic" @@ -58,14 +59,23 @@ type stmtSummaryByDigestMap struct { // It's rare to read concurrently, so RWMutex is not needed. sync.Mutex summaryMap *kvcache.SimpleLRUCache + // These fields are used for rolling summary. + beginTimeForCurInterval int64 + lastCheckExpireTime int64 - // enabledWrapper encapsulates variables needed to judge whether statement summary is enabled. - enabledWrapper struct { + // sysVars encapsulates system variables needed to control statement summary. + sysVars struct { sync.RWMutex // enabled indicates whether statement summary is enabled in current server. sessionEnabled string // setInSession indicates whether statement summary has been set in any session. globalEnabled string + // XXXRefreshInterval indicates the refresh interval of summaries. + // It must be > 0. + sessionRefreshInterval string + globalRefreshInterval string + // A cached result. It must be read atomically. + refreshInterval int64 } } @@ -76,6 +86,8 @@ var StmtSummaryByDigestMap = newStmtSummaryByDigestMap() type stmtSummaryByDigest struct { // It's rare to read concurrently, so RWMutex is not needed. sync.Mutex + // Each summary is summarized between [beginTime, beginTime + interval] + beginTime int64 // basic schemaName string digest string @@ -167,16 +179,21 @@ type StmtExecInfo struct { func newStmtSummaryByDigestMap() *stmtSummaryByDigestMap { maxStmtCount := config.GetGlobalConfig().StmtSummary.MaxStmtCount ssMap := &stmtSummaryByDigestMap{ - summaryMap: kvcache.NewSimpleLRUCache(maxStmtCount, 0, 0), + summaryMap: kvcache.NewSimpleLRUCache(maxStmtCount, 0, 0), + beginTimeForCurInterval: 0, + lastCheckExpireTime: 0, } - // enabledWrapper.defaultEnabled will be initialized in package variable. - ssMap.enabledWrapper.sessionEnabled = "" - ssMap.enabledWrapper.globalEnabled = "" + // sysVars.defaultEnabled will be initialized in package variable. + ssMap.sysVars.sessionEnabled = "" + ssMap.sysVars.globalEnabled = "" return ssMap } // AddStatement adds a statement to StmtSummaryByDigestMap. func (ssMap *stmtSummaryByDigestMap) AddStatement(sei *StmtExecInfo) { + // All times are count in seconds. + now := time.Now().Unix() + key := &stmtSummaryByDigestKey{ schemaName: sei.SchemaName, digest: sei.Digest, @@ -192,9 +209,25 @@ func (ssMap *stmtSummaryByDigestMap) AddStatement(sei *StmtExecInfo) { return nil, false } + // Check refreshing every second. + if now > ssMap.lastCheckExpireTime { + intervalSeconds := ssMap.RefreshInterval() + if ssMap.beginTimeForCurInterval+intervalSeconds <= now { + // `beginTimeForCurInterval` is a multiple of intervalSeconds, so that when the interval is a multiple + // of 60 (or 600, 1800, 3600, etc), begin time shows 'XX:XX:00', not 'XX:XX:01'~'XX:XX:59'. + ssMap.beginTimeForCurInterval = now / intervalSeconds * intervalSeconds + } + ssMap.lastCheckExpireTime = now + } + value, ok := ssMap.summaryMap.Get(key) + // Replacing an element in LRU cache can only be `Delete` + `Put`. + if ok && (value.(*stmtSummaryByDigest).beginTime < ssMap.beginTimeForCurInterval) { + ssMap.summaryMap.Delete(key) + ok = false + } if !ok { - newSummary := newStmtSummaryByDigest(sei) + newSummary := newStmtSummaryByDigest(sei, ssMap.beginTimeForCurInterval) ssMap.summaryMap.Put(key, newSummary) } return value, ok @@ -212,6 +245,8 @@ func (ssMap *stmtSummaryByDigestMap) Clear() { defer ssMap.Unlock() ssMap.summaryMap.DeleteAll() + ssMap.beginTimeForCurInterval = 0 + ssMap.lastCheckExpireTime = 0 } // ToDatum converts statement summary to datum. @@ -223,8 +258,10 @@ func (ssMap *stmtSummaryByDigestMap) ToDatum() [][]types.Datum { rows := make([][]types.Datum, 0, len(values)) for _, value := range values { ssbd := value.(*stmtSummaryByDigest) - record := ssbd.toDatum() - rows = append(rows, record) + record := ssbd.toDatum(ssMap.beginTimeForCurInterval) + if record != nil { + rows = append(rows, record) + } } return rows } @@ -256,15 +293,15 @@ func (ssMap *stmtSummaryByDigestMap) GetMoreThanOnceSelect() ([]string, []string func (ssMap *stmtSummaryByDigestMap) SetEnabled(value string, inSession bool) { value = ssMap.normalizeEnableValue(value) - ssMap.enabledWrapper.Lock() + ssMap.sysVars.Lock() if inSession { - ssMap.enabledWrapper.sessionEnabled = value + ssMap.sysVars.sessionEnabled = value } else { - ssMap.enabledWrapper.globalEnabled = value + ssMap.sysVars.globalEnabled = value } - sessionEnabled := ssMap.enabledWrapper.sessionEnabled - globalEnabled := ssMap.enabledWrapper.globalEnabled - ssMap.enabledWrapper.Unlock() + sessionEnabled := ssMap.sysVars.sessionEnabled + globalEnabled := ssMap.sysVars.globalEnabled + ssMap.sysVars.Unlock() // Clear all summaries once statement summary is disabled. var needClear bool @@ -280,14 +317,14 @@ func (ssMap *stmtSummaryByDigestMap) SetEnabled(value string, inSession bool) { // Enabled returns whether statement summary is enabled. func (ssMap *stmtSummaryByDigestMap) Enabled() bool { - ssMap.enabledWrapper.RLock() - defer ssMap.enabledWrapper.RUnlock() + ssMap.sysVars.RLock() + defer ssMap.sysVars.RUnlock() var enabled bool - if ssMap.isSet(ssMap.enabledWrapper.sessionEnabled) { - enabled = ssMap.isEnabled(ssMap.enabledWrapper.sessionEnabled) + if ssMap.isSet(ssMap.sysVars.sessionEnabled) { + enabled = ssMap.isEnabled(ssMap.sysVars.sessionEnabled) } else { - enabled = ssMap.isEnabled(ssMap.enabledWrapper.globalEnabled) + enabled = ssMap.isEnabled(ssMap.sysVars.globalEnabled) } return enabled } @@ -315,8 +352,44 @@ func (ssMap *stmtSummaryByDigestMap) isSet(value string) bool { return value != "" } +// SetRefreshInterval sets refreshing interval in ssMap.sysVars. +func (ssMap *stmtSummaryByDigestMap) SetRefreshInterval(value string, inSession bool) { + ssMap.sysVars.Lock() + if inSession { + ssMap.sysVars.sessionRefreshInterval = value + } else { + ssMap.sysVars.globalRefreshInterval = value + } + sessionRefreshInterval := ssMap.sysVars.sessionRefreshInterval + globalRefreshInterval := ssMap.sysVars.globalRefreshInterval + ssMap.sysVars.Unlock() + + // Calculate the cached `refreshInterval`. + var interval int + var err error + if ssMap.isSet(sessionRefreshInterval) { + interval, err = strconv.Atoi(sessionRefreshInterval) + if err != nil { + interval = 0 + } + } + if interval <= 0 { + interval, err = strconv.Atoi(globalRefreshInterval) + if err != nil { + interval = 0 + } + } + if interval > 0 { + atomic.StoreInt64(&ssMap.sysVars.refreshInterval, int64(interval)) + } +} + +func (ssMap *stmtSummaryByDigestMap) RefreshInterval() int64 { + return atomic.LoadInt64(&ssMap.sysVars.refreshInterval) +} + // newStmtSummaryByDigest creates a stmtSummaryByDigest from StmtExecInfo. -func newStmtSummaryByDigest(sei *StmtExecInfo) *stmtSummaryByDigest { +func newStmtSummaryByDigest(sei *StmtExecInfo, beginTime int64) *stmtSummaryByDigest { // Trim SQL to size MaxSQLLength. maxSQLLength := config.GetGlobalConfig().StmtSummary.MaxSQLLength normalizedSQL := sei.NormalizedSQL @@ -337,6 +410,7 @@ func newStmtSummaryByDigest(sei *StmtExecInfo) *stmtSummaryByDigest { tableNames := buffer.String() ssbd := &stmtSummaryByDigest{ + beginTime: beginTime, schemaName: sei.SchemaName, digest: sei.Digest, stmtType: strings.ToLower(sei.StmtCtx.StmtType), @@ -488,11 +562,16 @@ func (ssbd *stmtSummaryByDigest) add(sei *StmtExecInfo) { } } -func (ssbd *stmtSummaryByDigest) toDatum() []types.Datum { +func (ssbd *stmtSummaryByDigest) toDatum(oldestBeginTime int64) []types.Datum { ssbd.Lock() defer ssbd.Unlock() + if ssbd.beginTime < oldestBeginTime { + return nil + } + return types.MakeDatums( + types.Time{Time: types.FromGoTime(time.Unix(ssbd.beginTime, 0)), Type: mysql.TypeTimestamp}, ssbd.stmtType, ssbd.schemaName, ssbd.digest, diff --git a/util/stmtsummary/statement_summary_test.go b/util/stmtsummary/statement_summary_test.go index 745f4d3cf54a2..464994ad258c2 100644 --- a/util/stmtsummary/statement_summary_test.go +++ b/util/stmtsummary/statement_summary_test.go @@ -38,6 +38,7 @@ type testStmtSummarySuite struct { func (s *testStmtSummarySuite) SetUpSuite(c *C) { s.ssMap = newStmtSummaryByDigestMap() s.ssMap.SetEnabled("1", false) + s.ssMap.SetRefreshInterval("1800", false) } func TestT(t *testing.T) { @@ -48,6 +49,10 @@ func TestT(t *testing.T) { // Test stmtSummaryByDigest.AddStatement. func (s *testStmtSummarySuite) TestAddStatement(c *C) { s.ssMap.Clear() + now := time.Now().Unix() + s.ssMap.beginTimeForCurInterval = now + // to disable expiring + s.ssMap.lastCheckExpireTime = now + 60 tables := []stmtctx.TableEntry{{DB: "db1", Table: "tb1"}, {DB: "db2", Table: "tb2"}} indexes := []string{"a"} @@ -60,6 +65,7 @@ func (s *testStmtSummarySuite) TestAddStatement(c *C) { digest: stmtExecInfo1.Digest, } expectedSummary := stmtSummaryByDigest{ + beginTime: now, schemaName: stmtExecInfo1.SchemaName, stmtType: stmtExecInfo1.StmtCtx.StmtType, digest: stmtExecInfo1.Digest, @@ -360,7 +366,8 @@ func (s *testStmtSummarySuite) TestAddStatement(c *C) { } func matchStmtSummaryByDigest(first, second *stmtSummaryByDigest) bool { - if first.schemaName != second.schemaName || + if first.beginTime != second.beginTime || + first.schemaName != second.schemaName || !strings.EqualFold(first.stmtType, second.stmtType) || first.digest != second.digest || first.normalizedSQL != second.normalizedSQL || @@ -509,13 +516,17 @@ func generateAnyExecInfo() *StmtExecInfo { // Test stmtSummaryByDigest.ToDatum. func (s *testStmtSummarySuite) TestToDatum(c *C) { s.ssMap.Clear() + now := time.Now().Unix() + // to disable expiration + s.ssMap.beginTimeForCurInterval = now + 60 stmtExecInfo1 := generateAnyExecInfo() s.ssMap.AddStatement(stmtExecInfo1) datums := s.ssMap.ToDatum() c.Assert(len(datums), Equals, 1) + n := types.Time{Time: types.FromGoTime(time.Unix(s.ssMap.beginTimeForCurInterval, 0)), Type: mysql.TypeTimestamp} t := types.Time{Time: types.FromGoTime(stmtExecInfo1.StartTime), Type: mysql.TypeTimestamp} - match(c, datums[0], "select", stmtExecInfo1.SchemaName, stmtExecInfo1.Digest, stmtExecInfo1.NormalizedSQL, + match(c, datums[0], n, "select", stmtExecInfo1.SchemaName, stmtExecInfo1.Digest, stmtExecInfo1.NormalizedSQL, "db1.tb1,db2.tb2", "a", stmtExecInfo1.User, 1, int64(stmtExecInfo1.TotalLatency), int64(stmtExecInfo1.TotalLatency), int64(stmtExecInfo1.TotalLatency), int64(stmtExecInfo1.TotalLatency), int64(stmtExecInfo1.ParseLatency), int64(stmtExecInfo1.ParseLatency), int64(stmtExecInfo1.CompileLatency), @@ -543,6 +554,9 @@ func (s *testStmtSummarySuite) TestToDatum(c *C) { // Test AddStatement and ToDatum parallel. func (s *testStmtSummarySuite) TestAddStatementParallel(c *C) { s.ssMap.Clear() + now := time.Now().Unix() + // to disable expiration + s.ssMap.beginTimeForCurInterval = now + 60 threads := 8 loops := 32 @@ -576,6 +590,9 @@ func (s *testStmtSummarySuite) TestAddStatementParallel(c *C) { // Test max number of statement count. func (s *testStmtSummarySuite) TestMaxStmtCount(c *C) { s.ssMap.Clear() + now := time.Now().Unix() + // to disable expiration + s.ssMap.beginTimeForCurInterval = now + 60 stmtExecInfo1 := generateAnyExecInfo() maxStmtCount := config.GetGlobalConfig().StmtSummary.MaxStmtCount @@ -605,6 +622,9 @@ func (s *testStmtSummarySuite) TestMaxStmtCount(c *C) { // Test max length of normalized and sample SQL. func (s *testStmtSummarySuite) TestMaxSQLLength(c *C) { s.ssMap.Clear() + now := time.Now().Unix() + // to disable expiration + s.ssMap.beginTimeForCurInterval = now + 60 // Create a long SQL maxSQLLength := config.GetGlobalConfig().StmtSummary.MaxSQLLength @@ -631,9 +651,11 @@ func (s *testStmtSummarySuite) TestMaxSQLLength(c *C) { // Test setting EnableStmtSummary to 0. func (s *testStmtSummarySuite) TestDisableStmtSummary(c *C) { s.ssMap.Clear() + now := time.Now().Unix() // Set false in global scope, it should work. s.ssMap.SetEnabled("0", false) + s.ssMap.beginTimeForCurInterval = now + 60 stmtExecInfo1 := generateAnyExecInfo() s.ssMap.AddStatement(stmtExecInfo1) @@ -649,6 +671,7 @@ func (s *testStmtSummarySuite) TestDisableStmtSummary(c *C) { // Set false in global scope, it shouldn't work. s.ssMap.SetEnabled("0", false) + s.ssMap.beginTimeForCurInterval = now + 60 stmtExecInfo2 := stmtExecInfo1 stmtExecInfo2.OriginalSQL = "original_sql2" @@ -660,12 +683,14 @@ func (s *testStmtSummarySuite) TestDisableStmtSummary(c *C) { // Unset in session scope. s.ssMap.SetEnabled("", true) + s.ssMap.beginTimeForCurInterval = now + 60 s.ssMap.AddStatement(stmtExecInfo2) datums = s.ssMap.ToDatum() c.Assert(len(datums), Equals, 0) // Unset in global scope. s.ssMap.SetEnabled("", false) + s.ssMap.beginTimeForCurInterval = now + 60 s.ssMap.AddStatement(stmtExecInfo1) datums = s.ssMap.ToDatum() c.Assert(len(datums), Equals, 0) @@ -677,6 +702,9 @@ func (s *testStmtSummarySuite) TestDisableStmtSummary(c *C) { // Test GetMoreThanOnceSelect. func (s *testStmtSummarySuite) TestGetMoreThenOnceSelect(c *C) { s.ssMap.Clear() + now := time.Now().Unix() + // to disable expiration + s.ssMap.beginTimeForCurInterval = now + 60 stmtExecInfo1 := generateAnyExecInfo() stmtExecInfo1.OriginalSQL = "insert 1" @@ -712,3 +740,32 @@ func (s *testStmtSummarySuite) TestFormatBackoffTypes(c *C) { backoffMap[tikv.BoTxnLock] = 2 c.Assert(formatBackoffTypes(backoffMap), Equals, "txnLock:2,pdRPC:1") } + +// Test refreshing current statement summary periodically. +func (s *testStmtSummarySuite) TestRefreshCurrentSummary(c *C) { + s.ssMap.Clear() + now := time.Now().Unix() + + s.ssMap.beginTimeForCurInterval = now + 10 + stmtExecInfo1 := generateAnyExecInfo() + key := &stmtSummaryByDigestKey{ + schemaName: stmtExecInfo1.SchemaName, + digest: stmtExecInfo1.Digest, + } + s.ssMap.AddStatement(stmtExecInfo1) + c.Assert(s.ssMap.summaryMap.Size(), Equals, 1) + value, ok := s.ssMap.summaryMap.Get(key) + c.Assert(ok, IsTrue) + c.Assert(value.(*stmtSummaryByDigest).beginTime, Equals, s.ssMap.beginTimeForCurInterval) + c.Assert(value.(*stmtSummaryByDigest).execCount, Equals, int64(1)) + + s.ssMap.beginTimeForCurInterval = now - 1900 + value.(*stmtSummaryByDigest).beginTime = now - 1900 + s.ssMap.lastCheckExpireTime = now - 10 + s.ssMap.AddStatement(stmtExecInfo1) + c.Assert(s.ssMap.summaryMap.Size(), Equals, 1) + value, ok = s.ssMap.summaryMap.Get(key) + c.Assert(ok, IsTrue) + c.Assert(value.(*stmtSummaryByDigest).beginTime, Greater, now-1900) + c.Assert(value.(*stmtSummaryByDigest).execCount, Equals, int64(1)) +} From 51df36e9c347489f8a7c6ab1ad93ed7c250e4756 Mon Sep 17 00:00:00 2001 From: djshow832 <873581766@qq.com> Date: Fri, 22 Nov 2019 11:06:39 +0800 Subject: [PATCH 2/7] enable stmt summary --- sessionctx/variable/sysvar.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index 22b042941948e..5467ddd660d6d 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -716,7 +716,7 @@ var defaultSysVars = []*SysVar{ {ScopeGlobal | ScopeSession, TiDBEnableNoopFuncs, BoolToIntStr(DefTiDBEnableNoopFuncs)}, {ScopeSession, TiDBReplicaRead, "leader"}, {ScopeSession, TiDBAllowRemoveAutoInc, BoolToIntStr(DefTiDBAllowRemoveAutoInc)}, - {ScopeGlobal | ScopeSession, TiDBEnableStmtSummary, "0"}, + {ScopeGlobal | ScopeSession, TiDBEnableStmtSummary, "1"}, {ScopeGlobal | ScopeSession, TiDBStmtSummaryRefreshInterval, strconv.Itoa(DefTiDBStmtSummaryRefreshInterval)}, {ScopeGlobal | ScopeSession, TiDBCapturePlanBaseline, "0"}, {ScopeGlobal | ScopeSession, TiDBUsePlanBaselines, BoolToIntStr(DefTiDBUsePlanBaselines)}, From be43cde8e46edee894ed4e3dfa971299a200b084 Mon Sep 17 00:00:00 2001 From: djshow832 <873581766@qq.com> Date: Fri, 22 Nov 2019 22:20:39 +0800 Subject: [PATCH 3/7] disable stmt summary --- sessionctx/variable/sysvar.go | 2 +- util/stmtsummary/statement_summary.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index 5467ddd660d6d..22b042941948e 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -716,7 +716,7 @@ var defaultSysVars = []*SysVar{ {ScopeGlobal | ScopeSession, TiDBEnableNoopFuncs, BoolToIntStr(DefTiDBEnableNoopFuncs)}, {ScopeSession, TiDBReplicaRead, "leader"}, {ScopeSession, TiDBAllowRemoveAutoInc, BoolToIntStr(DefTiDBAllowRemoveAutoInc)}, - {ScopeGlobal | ScopeSession, TiDBEnableStmtSummary, "1"}, + {ScopeGlobal | ScopeSession, TiDBEnableStmtSummary, "0"}, {ScopeGlobal | ScopeSession, TiDBStmtSummaryRefreshInterval, strconv.Itoa(DefTiDBStmtSummaryRefreshInterval)}, {ScopeGlobal | ScopeSession, TiDBCapturePlanBaseline, "0"}, {ScopeGlobal | ScopeSession, TiDBUsePlanBaselines, BoolToIntStr(DefTiDBUsePlanBaselines)}, diff --git a/util/stmtsummary/statement_summary.go b/util/stmtsummary/statement_summary.go index 30b4f90b91887..67b108fbec5b1 100644 --- a/util/stmtsummary/statement_summary.go +++ b/util/stmtsummary/statement_summary.go @@ -212,6 +212,10 @@ func (ssMap *stmtSummaryByDigestMap) AddStatement(sei *StmtExecInfo) { // Check refreshing every second. if now > ssMap.lastCheckExpireTime { intervalSeconds := ssMap.RefreshInterval() + if intervalSeconds <= 0 { + return nil, false + } + if ssMap.beginTimeForCurInterval+intervalSeconds <= now { // `beginTimeForCurInterval` is a multiple of intervalSeconds, so that when the interval is a multiple // of 60 (or 600, 1800, 3600, etc), begin time shows 'XX:XX:00', not 'XX:XX:01'~'XX:XX:59'. From d3f739d8bea0364d31575d85b4f3aca604aa58bf Mon Sep 17 00:00:00 2001 From: djshow832 <873581766@qq.com> Date: Tue, 26 Nov 2019 15:52:01 +0800 Subject: [PATCH 4/7] address comment --- infoschema/perfschema/tables_test.go | 4 ++-- sessionctx/variable/tidb_vars.go | 2 +- sessionctx/variable/varsutil.go | 9 +-------- util/stmtsummary/statement_summary.go | 2 +- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/infoschema/perfschema/tables_test.go b/infoschema/perfschema/tables_test.go index 0827e72b99022..c92f80682ebfd 100644 --- a/infoschema/perfschema/tables_test.go +++ b/infoschema/perfschema/tables_test.go @@ -82,8 +82,8 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) { // Invalidate the cache manually so that tidb_enable_stmt_summary works immediately. s.dom.GetGlobalVarsCache().Disable() // Disable refreshing summary. - tk.MustExec("set global tidb_stmt_summary_refresh_interval = 9999999999") - tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("9999999999")) + tk.MustExec("set global tidb_stmt_summary_refresh_interval = 999999999") + tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("999999999")) // Create a new session to test. tk = testkit.NewTestKitWithInit(c, s.store) diff --git a/sessionctx/variable/tidb_vars.go b/sessionctx/variable/tidb_vars.go index a316a6e5775ef..11ac625d62861 100644 --- a/sessionctx/variable/tidb_vars.go +++ b/sessionctx/variable/tidb_vars.go @@ -429,7 +429,7 @@ const ( DefWaitSplitRegionTimeout = 300 // 300s DefTiDBEnableNoopFuncs = false DefTiDBAllowRemoveAutoInc = false - DefTiDBStmtSummaryRefreshInterval = 1800 + DefTiDBStmtSummaryRefreshInterval = 1800 // 1800s DefTiDBUsePlanBaselines = true DefTiDBEvolvePlanBaselines = false DefTiDBEvolvePlanTaskMaxTime = 600 // 600s diff --git a/sessionctx/variable/varsutil.go b/sessionctx/variable/varsutil.go index 5148dd5338c97..954a597a4e5fe 100644 --- a/sessionctx/variable/varsutil.go +++ b/sessionctx/variable/varsutil.go @@ -633,14 +633,7 @@ func ValidateSetSystemVar(vars *SessionVars, name string, value string) (string, if value == "" { return "", nil } - v, err := strconv.Atoi(value) - if err != nil { - return value, ErrWrongTypeForVar.GenWithStackByArgs(name) - } - if v <= 0 { - return value, ErrWrongValueForVar.GenWithStackByArgs(name, value) - } - return value, nil + return checkUInt64SystemVar(name, value, 1, math.MaxUint32, vars) case TiDBIsolationReadEngines: engines := strings.Split(value, ",") var formatVal string diff --git a/util/stmtsummary/statement_summary.go b/util/stmtsummary/statement_summary.go index 67b108fbec5b1..9c0118b3586c2 100644 --- a/util/stmtsummary/statement_summary.go +++ b/util/stmtsummary/statement_summary.go @@ -86,7 +86,7 @@ var StmtSummaryByDigestMap = newStmtSummaryByDigestMap() type stmtSummaryByDigest struct { // It's rare to read concurrently, so RWMutex is not needed. sync.Mutex - // Each summary is summarized between [beginTime, beginTime + interval] + // Each summary is summarized between [beginTime, beginTime + interval]. beginTime int64 // basic schemaName string From cfa8d9f8a3d46e915369db8c16a6c4fa5fd32404 Mon Sep 17 00:00:00 2001 From: djshow832 <873581766@qq.com> Date: Tue, 26 Nov 2019 16:07:59 +0800 Subject: [PATCH 5/7] fix UT --- sessionctx/variable/varsutil_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/sessionctx/variable/varsutil_test.go b/sessionctx/variable/varsutil_test.go index 0e305bbe18abe..f2451f62b39ad 100644 --- a/sessionctx/variable/varsutil_test.go +++ b/sessionctx/variable/varsutil_test.go @@ -476,7 +476,6 @@ func (s *testVarsutilSuite) TestValidate(c *C) { {TiDBEnableStmtSummary, "a", true}, {TiDBEnableStmtSummary, "-1", true}, {TiDBEnableStmtSummary, "", false}, - {TiDBStmtSummaryRefreshInterval, "0", true}, {TiDBStmtSummaryRefreshInterval, "a", true}, {TiDBStmtSummaryRefreshInterval, "", false}, } From 028a52654aeb7fce9c014ac10f2358642f880954 Mon Sep 17 00:00:00 2001 From: djshow832 <873581766@qq.com> Date: Wed, 27 Nov 2019 17:14:33 +0800 Subject: [PATCH 6/7] address comment --- util/stmtsummary/statement_summary.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/stmtsummary/statement_summary.go b/util/stmtsummary/statement_summary.go index 9c0118b3586c2..eba1b60100fcc 100644 --- a/util/stmtsummary/statement_summary.go +++ b/util/stmtsummary/statement_summary.go @@ -33,7 +33,7 @@ import ( ) // There're many types of statement summary tables in MySQL, but we have -// only implemented events_statement_summary_by_digest for now. +// only implemented events_statements_summary_by_digest for now. // stmtSummaryByDigestKey defines key for stmtSummaryByDigestMap.summaryMap. type stmtSummaryByDigestKey struct { From 52db69026d00b5d55d78e344514584ea1e44b21d Mon Sep 17 00:00:00 2001 From: djshow832 <873581766@qq.com> Date: Thu, 28 Nov 2019 15:07:48 +0800 Subject: [PATCH 7/7] address comment --- util/stmtsummary/statement_summary.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/stmtsummary/statement_summary.go b/util/stmtsummary/statement_summary.go index eba1b60100fcc..a4cba7a67c604 100644 --- a/util/stmtsummary/statement_summary.go +++ b/util/stmtsummary/statement_summary.go @@ -70,8 +70,8 @@ type stmtSummaryByDigestMap struct { sessionEnabled string // setInSession indicates whether statement summary has been set in any session. globalEnabled string - // XXXRefreshInterval indicates the refresh interval of summaries. - // It must be > 0. + // These variables indicate the refresh interval of summaries. + // They must be > 0. sessionRefreshInterval string globalRefreshInterval string // A cached result. It must be read atomically. @@ -191,7 +191,7 @@ func newStmtSummaryByDigestMap() *stmtSummaryByDigestMap { // AddStatement adds a statement to StmtSummaryByDigestMap. func (ssMap *stmtSummaryByDigestMap) AddStatement(sei *StmtExecInfo) { - // All times are count in seconds. + // All times are counted in seconds. now := time.Now().Unix() key := &stmtSummaryByDigestKey{