diff --git a/executor/adapter.go b/executor/adapter.go index 892dcf5fe02f8..4452ebc131ae6 100644 --- a/executor/adapter.go +++ b/executor/adapter.go @@ -977,5 +977,6 @@ func (a *ExecStmt) SummaryStmt(succ bool) { StartTime: sessVars.StartTime, IsInternal: sessVars.InRestrictedSQL, Succeed: succ, + PlanInCache: sessVars.FoundInPlanCache, }) } diff --git a/infoschema/perfschema/const.go b/infoschema/perfschema/const.go index 5ce6ab82c8278..86a5f1766739f 100644 --- a/infoschema/perfschema/const.go +++ b/infoschema/perfschema/const.go @@ -413,6 +413,8 @@ const tableEventsStatementsSummaryByDigest = "CREATE TABLE if not exists perform "SUM_NO_GOOD_INDEX_USED bigint unsigned NOT NULL," + "FIRST_SEEN timestamp(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000'," + "LAST_SEEN timestamp(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000'," + + "PLAN_IN_CACHE bool NOT NULL," + + "PLAN_CACHE_HITS bigint unsigned NOT NULL," + "QUANTILE_95 bigint unsigned NOT NULL," + "QUANTILE_99 bigint unsigned NOT NULL," + "QUANTILE_999 bigint unsigned NOT NULL," + diff --git a/infoschema/tables.go b/infoschema/tables.go index c1b4b7aa4fd14..82ca41934bd2c 100644 --- a/infoschema/tables.go +++ b/infoschema/tables.go @@ -1194,6 +1194,8 @@ var tableStatementsSummaryCols = []columnInfo{ {name: "AVG_AFFECTED_ROWS", tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rows affected"}, {name: "FIRST_SEEN", tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "The time these statements are seen for the first time"}, {name: "LAST_SEEN", tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "The time these statements are seen for the last time"}, + {name: "PLAN_IN_CACHE", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, comment: "Whether the last statement hit plan cache"}, + {name: "PLAN_CACHE_HITS", tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag, comment: "The number of times these statements hit plan cache"}, {name: "QUERY_SAMPLE_TEXT", tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Sampled original statement"}, {name: "PREV_SAMPLE_TEXT", tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "The previous statement before commit"}, {name: "PLAN_DIGEST", tp: mysql.TypeVarchar, size: 64, comment: "Digest of its execution plan"}, diff --git a/infoschema/tables_test.go b/infoschema/tables_test.go index 9e27ec1eb614e..67407e8f60a87 100644 --- a/infoschema/tables_test.go +++ b/infoschema/tables_test.go @@ -16,6 +16,7 @@ package infoschema_test import ( "crypto/tls" "fmt" + "math" "net" "net/http/httptest" "os" @@ -36,11 +37,13 @@ import ( "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/meta/autoid" + plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/server" "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/store/helper" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/util/kvcache" "github.com/pingcap/tidb/util/pdapi" "github.com/pingcap/tidb/util/set" "github.com/pingcap/tidb/util/testkit" @@ -84,6 +87,18 @@ func (s *testTableSuiteBase) newTestKitWithRoot(c *C) *testkit.TestKit { return tk } +func (s *testTableSuiteBase) newTestKitWithPlanCache(c *C) *testkit.TestKit { + tk := testkit.NewTestKit(c, s.store) + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + tk.GetConnectionID() + c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) + return tk +} + type testClusterTableSuite struct { *testTableSuiteBase rpcserver *grpc.Server @@ -1231,3 +1246,29 @@ func (s *testTableSuite) TestStmtSummaryPreparedStatements(c *C) { from information_schema.statements_summary where digest_text like "select ?"`).Check(testkit.Rows("1")) } + +func (s *testTableSuite) TestPerformanceSchemaforPlanCache(c *C) { + orgEnable := plannercore.PreparedPlanCacheEnabled() + defer func() { + plannercore.SetPreparedPlanCache(orgEnable) + }() + plannercore.SetPreparedPlanCache(true) + + tk := s.newTestKitWithPlanCache(c) + + // Clear summaries. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("prepare stmt from 'select * from t'") + tk.MustExec("execute stmt") + tk.MustQuery("select plan_cache_hits, plan_in_cache from information_schema.statements_summary where digest_text='select * from t'").Check( + testkit.Rows("0 0")) + tk.MustExec("execute stmt") + tk.MustExec("execute stmt") + tk.MustExec("execute stmt") + tk.MustQuery("select plan_cache_hits, plan_in_cache from information_schema.statements_summary where digest_text='select * from t'").Check( + testkit.Rows("3 1")) +} diff --git a/util/stmtsummary/statement_summary.go b/util/stmtsummary/statement_summary.go index 04c1f741a692f..98f07ca3becd1 100644 --- a/util/stmtsummary/statement_summary.go +++ b/util/stmtsummary/statement_summary.go @@ -168,6 +168,9 @@ type stmtSummaryByDigestElement struct { firstSeen time.Time // The last time this type of SQL executes. lastSeen time.Time + // plan cache + planInCache bool + planCacheHits int64 } // StmtExecInfo records execution information of each statement. @@ -192,6 +195,7 @@ type StmtExecInfo struct { StartTime time.Time IsInternal bool Succeed bool + PlanInCache bool } // newStmtSummaryByDigestMap creates an empty stmtSummaryByDigestMap. @@ -568,13 +572,15 @@ func newStmtSummaryByDigestElement(sei *StmtExecInfo, beginTime int64, intervalS // PrevSQL is already truncated to cfg.Log.QueryLogMaxLen. prevSQL: sei.PrevSQL, // samplePlan needs to be decoded so it can't be truncated. - samplePlan: sei.PlanGenerator(), - indexNames: sei.StmtCtx.IndexNames, - minLatency: sei.TotalLatency, - firstSeen: sei.StartTime, - lastSeen: sei.StartTime, - backoffTypes: make(map[fmt.Stringer]int), - authUsers: make(map[string]struct{}), + samplePlan: sei.PlanGenerator(), + indexNames: sei.StmtCtx.IndexNames, + minLatency: sei.TotalLatency, + firstSeen: sei.StartTime, + lastSeen: sei.StartTime, + backoffTypes: make(map[fmt.Stringer]int), + authUsers: make(map[string]struct{}), + planInCache: false, + planCacheHits: 0, } ssElement.add(sei, intervalSeconds) return ssElement @@ -721,6 +727,12 @@ func (ssElement *stmtSummaryByDigestElement) add(sei *StmtExecInfo, intervalSeco commitDetails.Mu.Unlock() } + //plan cache + if sei.PlanInCache { + ssElement.planInCache = true + ssElement.planCacheHits += 1 + } + // other ssElement.sumAffectedRows += sei.StmtCtx.AffectedRows() ssElement.sumMem += sei.MemMax @@ -815,6 +827,8 @@ func (ssElement *stmtSummaryByDigestElement) toDatum(ssbd *stmtSummaryByDigest) avgFloat(int64(ssElement.sumAffectedRows), ssElement.execCount), types.NewTime(types.FromGoTime(ssElement.firstSeen), mysql.TypeTimestamp, 0), types.NewTime(types.FromGoTime(ssElement.lastSeen), mysql.TypeTimestamp, 0), + ssElement.planInCache, + ssElement.planCacheHits, ssElement.sampleSQL, ssElement.prevSQL, ssbd.planDigest, diff --git a/util/stmtsummary/statement_summary_test.go b/util/stmtsummary/statement_summary_test.go index 1b8eb6f2d98cb..5dde31d1d1308 100644 --- a/util/stmtsummary/statement_summary_test.go +++ b/util/stmtsummary/statement_summary_test.go @@ -606,7 +606,7 @@ func (s *testStmtSummarySuite) TestToDatum(c *C) { stmtExecInfo1.ExecDetail.CommitDetail.PrewriteRegionNum, stmtExecInfo1.ExecDetail.CommitDetail.PrewriteRegionNum, stmtExecInfo1.ExecDetail.CommitDetail.TxnRetry, stmtExecInfo1.ExecDetail.CommitDetail.TxnRetry, 1, "txnLock:1", stmtExecInfo1.MemMax, stmtExecInfo1.MemMax, stmtExecInfo1.StmtCtx.AffectedRows(), - t, t, stmtExecInfo1.OriginalSQL, stmtExecInfo1.PrevSQL, "plan_digest", ""} + t, t, 0, 0, stmtExecInfo1.OriginalSQL, stmtExecInfo1.PrevSQL, "plan_digest", ""} match(c, datums[0], expectedDatum...) datums = s.ssMap.ToHistoryDatum(nil, true) diff --git a/util/testkit/testkit.go b/util/testkit/testkit.go index cd7c389385437..1ae061c3b0adc 100644 --- a/util/testkit/testkit.go +++ b/util/testkit/testkit.go @@ -129,14 +129,21 @@ func NewTestKitWithInit(c *check.C, store kv.Storage) *TestKit { var connectionID uint64 +// GetConnectionID get the connection ID for tk.Se +func (tk *TestKit) GetConnectionID() { + if tk.Se != nil { + id := atomic.AddUint64(&connectionID, 1) + tk.Se.SetConnectionID(id) + } +} + // Exec executes a sql statement. func (tk *TestKit) Exec(sql string, args ...interface{}) (sqlexec.RecordSet, error) { var err error if tk.Se == nil { tk.Se, err = session.CreateSession4Test(tk.store) tk.c.Assert(err, check.IsNil) - id := atomic.AddUint64(&connectionID, 1) - tk.Se.SetConnectionID(id) + tk.GetConnectionID() } ctx := context.Background() if len(args) == 0 {