Skip to content

Commit fd3ada6

Browse files
djshow832sre-bot
authored andcommitted
infoschema: add plan field to the statement summary tables (pingcap#14182)
1 parent 7a024a2 commit fd3ada6

File tree

8 files changed

+140
-62
lines changed

8 files changed

+140
-62
lines changed

config/config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ var defaultConf = Config{
547547
},
548548
StmtSummary: StmtSummary{
549549
Enable: false,
550-
MaxStmtCount: 100,
550+
MaxStmtCount: 200,
551551
MaxSQLLength: 4096,
552552
RefreshInterval: 1800,
553553
HistorySize: 24,

config/config.toml.example

+1-1
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ max-retry-count = 256
351351
enable = false
352352

353353
# max number of statements kept in memory.
354-
max-stmt-count = 100
354+
max-stmt-count = 200
355355

356356
# max length of displayed normalized sql and sample sql.
357357
max-sql-length = 4096

executor/adapter.go

+8
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,12 @@ func (a *ExecStmt) SummaryStmt() {
876876
}
877877
sessVars.SetPrevStmtDigest(digest)
878878

879+
// No need to encode every time, so encode lazily.
880+
planGenerator := func() string {
881+
return plannercore.EncodePlan(a.Plan)
882+
}
883+
_, planDigest := getPlanDigest(a.Ctx, a.Plan)
884+
879885
execDetail := stmtCtx.GetExecDetails()
880886
copTaskInfo := stmtCtx.CopTasksDetails()
881887
memMax := stmtCtx.MemTracker.MaxConsumed()
@@ -891,6 +897,8 @@ func (a *ExecStmt) SummaryStmt() {
891897
Digest: digest,
892898
PrevSQL: prevSQL,
893899
PrevSQLDigest: prevSQLDigest,
900+
PlanGenerator: planGenerator,
901+
PlanDigest: planDigest,
894902
User: userString,
895903
TotalLatency: costTime,
896904
ParseLatency: sessVars.DurationParse,

infoschema/perfschema/const.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,9 @@ const fieldsInEventsStatementsSummary = " (" +
447447
"FIRST_SEEN TIMESTAMP(6) NOT NULL," +
448448
"LAST_SEEN TIMESTAMP(6) NOT NULL," +
449449
"QUERY_SAMPLE_TEXT LONGTEXT DEFAULT NULL," +
450-
"PREV_SAMPLE_TEXT LONGTEXT DEFAULT NULL);"
450+
"PREV_SAMPLE_TEXT LONGTEXT DEFAULT NULL," +
451+
"PLAN_DIGEST VARCHAR(64) DEFAULT NULL," +
452+
"PLAN LONGTEXT DEFAULT NULL);"
451453

452454
// tableEventsStatementsSummaryByDigest contains the column name definitions for table
453455
// events_statements_summary_by_digest, same as MySQL.

infoschema/perfschema/tables_test.go

+40-19
Original file line numberDiff line numberDiff line change
@@ -106,27 +106,44 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) {
106106
tk.MustExec("/**/insert into t values(4, 'd')")
107107
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
108108
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
109-
max_prewrite_regions, avg_affected_rows, query_sample_text
109+
max_prewrite_regions, avg_affected_rows, query_sample_text, plan
110110
from performance_schema.events_statements_summary_by_digest
111111
where digest_text like 'insert into t%'`,
112-
).Check(testkit.Rows("insert test test.t <nil> 4 0 0 0 0 0 2 2 1 1 1 /**/insert into t values(4, 'd')"))
112+
).Check(testkit.Rows("insert test test.t <nil> 4 0 0 0 0 0 2 2 1 1 1 insert into t values(1, 'a') "))
113113

114114
// Test SELECT.
115+
const failpointName = "github.com/pingcap/tidb/planner/core/mockPlanRowCount"
116+
c.Assert(failpoint.Enable(failpointName, "return(100)"), IsNil)
117+
defer func() { c.Assert(failpoint.Disable(failpointName), IsNil) }()
115118
tk.MustQuery("select * from t where a=2")
116119
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
117120
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
118-
max_prewrite_regions, avg_affected_rows, query_sample_text
121+
max_prewrite_regions, avg_affected_rows, query_sample_text, plan
119122
from performance_schema.events_statements_summary_by_digest
120123
where digest_text like 'select * from t%'`,
121-
).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"))
124+
).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 \tIndexLookUp_10\troot\t100\t\n" +
125+
"\t├─IndexScan_8 \tcop \t100\ttable:t, index:a, range:[2,2], keep order:false, stats:pseudo\n" +
126+
"\t└─TableScan_9 \tcop \t100\ttable:t, keep order:false, stats:pseudo"))
122127

123128
// select ... order by
124129
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
125130
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
126-
max_prewrite_regions, avg_affected_rows, query_sample_text
131+
max_prewrite_regions, avg_affected_rows, query_sample_text, plan
127132
from performance_schema.events_statements_summary_by_digest
128133
order by exec_count desc limit 1`,
129-
).Check(testkit.Rows("insert test test.t <nil> 4 0 0 0 0 0 2 2 1 1 1 /**/insert into t values(4, 'd')"))
134+
).Check(testkit.Rows("insert test test.t <nil> 4 0 0 0 0 0 2 2 1 1 1 insert into t values(1, 'a') "))
135+
136+
// Test different plans with same digest.
137+
c.Assert(failpoint.Enable(failpointName, "return(1000)"), IsNil)
138+
tk.MustQuery("select * from t where a=3")
139+
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
140+
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
141+
max_prewrite_regions, avg_affected_rows, query_sample_text, plan
142+
from performance_schema.events_statements_summary_by_digest
143+
where digest_text like 'select * from t%'`,
144+
).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 \tIndexLookUp_10\troot\t100\t\n" +
145+
"\t├─IndexScan_8 \tcop \t100\ttable:t, index:a, range:[2,2], keep order:false, stats:pseudo\n" +
146+
"\t└─TableScan_9 \tcop \t100\ttable:t, keep order:false, stats:pseudo"))
130147

131148
// Disable it again.
132149
tk.MustExec("set global tidb_enable_stmt_summary = false")
@@ -141,7 +158,7 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) {
141158
// The table should be cleared.
142159
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
143160
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
144-
max_prewrite_regions, avg_affected_rows, query_sample_text
161+
max_prewrite_regions, avg_affected_rows, query_sample_text, plan
145162
from performance_schema.events_statements_summary_by_digest`,
146163
).Check(testkit.Rows())
147164

@@ -153,24 +170,26 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) {
153170
tk.MustExec("commit")
154171
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
155172
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
156-
max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text
173+
max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text, plan
157174
from performance_schema.events_statements_summary_by_digest
158175
where digest_text like 'insert into t%'`,
159-
).Check(testkit.Rows("insert test test.t <nil> 1 0 0 0 0 0 0 0 0 0 1 insert into t values(1, 'a') "))
176+
).Check(testkit.Rows("insert test test.t <nil> 1 0 0 0 0 0 0 0 0 0 1 insert into t values(1, 'a') "))
160177
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
161178
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
162-
max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text
179+
max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text, plan
163180
from performance_schema.events_statements_summary_by_digest
164181
where digest_text='commit'`,
165-
).Check(testkit.Rows("commit test <nil> <nil> 1 0 0 0 0 0 2 2 1 1 0 commit insert into t values(1, 'a')"))
182+
).Check(testkit.Rows("commit test <nil> <nil> 1 0 0 0 0 0 2 2 1 1 0 commit insert into t values(1, 'a') "))
166183

167184
tk.MustQuery("select * from t where a=2")
168185
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
169186
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
170-
max_prewrite_regions, avg_affected_rows, query_sample_text
187+
max_prewrite_regions, avg_affected_rows, query_sample_text, plan
171188
from performance_schema.events_statements_summary_by_digest
172189
where digest_text like 'select * from t%'`,
173-
).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"))
190+
).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 \tIndexLookUp_10\troot\t1000\t\n" +
191+
"\t├─IndexScan_8 \tcop \t1000\ttable:t, index:a, range:[2,2], keep order:false, stats:pseudo\n" +
192+
"\t└─TableScan_9 \tcop \t1000\ttable:t, keep order:false, stats:pseudo"))
174193

175194
// Disable it in global scope.
176195
tk.MustExec("set global tidb_enable_stmt_summary = off")
@@ -183,10 +202,12 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) {
183202
// Statement summary is still enabled.
184203
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
185204
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
186-
max_prewrite_regions, avg_affected_rows, query_sample_text
205+
max_prewrite_regions, avg_affected_rows, query_sample_text, plan
187206
from performance_schema.events_statements_summary_by_digest
188207
where digest_text like 'select * from t%'`,
189-
).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"))
208+
).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 \tIndexLookUp_10\troot\t1000\t\n" +
209+
"\t├─IndexScan_8 \tcop \t1000\ttable:t, index:a, range:[2,2], keep order:false, stats:pseudo\n" +
210+
"\t└─TableScan_9 \tcop \t1000\ttable:t, keep order:false, stats:pseudo"))
190211

191212
// Unset session variable.
192213
tk.MustExec("set session tidb_enable_stmt_summary = ''")
@@ -195,7 +216,7 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) {
195216
// Statement summary is disabled.
196217
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
197218
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
198-
max_prewrite_regions, avg_affected_rows, query_sample_text
219+
max_prewrite_regions, avg_affected_rows, query_sample_text, plan
199220
from performance_schema.events_statements_summary_by_digest`,
200221
).Check(testkit.Rows())
201222
}
@@ -232,15 +253,15 @@ func (s *testTableSuite) TestStmtSummaryHistoryTable(c *C) {
232253
tk.MustExec("/**/insert into t values(4, 'd')")
233254
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
234255
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
235-
max_prewrite_regions, avg_affected_rows, query_sample_text
256+
max_prewrite_regions, avg_affected_rows, query_sample_text, plan
236257
from performance_schema.events_statements_summary_by_digest_history
237258
where digest_text like 'insert into t%'`,
238-
).Check(testkit.Rows("insert test test.t <nil> 4 0 0 0 0 0 2 2 1 1 1 /**/insert into t values(4, 'd')"))
259+
).Check(testkit.Rows("insert test test.t <nil> 4 0 0 0 0 0 2 2 1 1 1 insert into t values(1, 'a') "))
239260

240261
tk.MustExec("set global tidb_stmt_summary_history_size = 0")
241262
tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, cop_task_num, avg_total_keys,
242263
max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions,
243-
max_prewrite_regions, avg_affected_rows, query_sample_text
264+
max_prewrite_regions, avg_affected_rows, query_sample_text, plan
244265
from performance_schema.events_statements_summary_by_digest_history`,
245266
).Check(testkit.Rows())
246267
}

planner/core/encode.go

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"hash"
2121
"sync"
2222

23+
"github.com/pingcap/failpoint"
2324
"github.com/pingcap/tidb/util/plancodec"
2425
)
2526

@@ -42,6 +43,9 @@ func EncodePlan(p Plan) string {
4243
if selectPlan == nil {
4344
return ""
4445
}
46+
failpoint.Inject("mockPlanRowCount", func(val failpoint.Value) {
47+
selectPlan.statsInfo().RowCount = float64(val.(int))
48+
})
4549
return pn.encodePlanTree(selectPlan)
4650
}
4751

util/stmtsummary/statement_summary.go

+42-28
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ import (
3131
"github.com/pingcap/tidb/util/execdetails"
3232
"github.com/pingcap/tidb/util/hack"
3333
"github.com/pingcap/tidb/util/kvcache"
34+
"github.com/pingcap/tidb/util/logutil"
35+
"github.com/pingcap/tidb/util/plancodec"
36+
"go.uber.org/zap"
3437
)
3538

3639
// There're many types of statement summary tables in MySQL, but we have
@@ -43,6 +46,8 @@ type stmtSummaryByDigestKey struct {
4346
digest string
4447
// The digest of the previous statement.
4548
prevDigest string
49+
// The digest of the plan of this SQL.
50+
planDigest string
4651
// `hash` is the hash value of this object.
4752
hash []byte
4853
}
@@ -52,10 +57,11 @@ type stmtSummaryByDigestKey struct {
5257
// `prevSQL` is included in the key To distinguish different transactions.
5358
func (key *stmtSummaryByDigestKey) Hash() []byte {
5459
if len(key.hash) == 0 {
55-
key.hash = make([]byte, 0, len(key.schemaName)+len(key.digest)+len(key.prevDigest))
60+
key.hash = make([]byte, 0, len(key.schemaName)+len(key.digest)+len(key.prevDigest)+len(key.planDigest))
5661
key.hash = append(key.hash, hack.Slice(key.digest)...)
5762
key.hash = append(key.hash, hack.Slice(key.schemaName)...)
5863
key.hash = append(key.hash, hack.Slice(key.prevDigest)...)
64+
key.hash = append(key.hash, hack.Slice(key.planDigest)...)
5965
}
6066
return key.hash
6167
}
@@ -106,6 +112,7 @@ type stmtSummaryByDigest struct {
106112
// They won't change once this object is created, so locking is not needed.
107113
schemaName string
108114
digest string
115+
planDigest string
109116
stmtType string
110117
normalizedSQL string
111118
tableNames string
@@ -120,6 +127,7 @@ type stmtSummaryByDigestElement struct {
120127
// basic
121128
sampleSQL string
122129
prevSQL string
130+
samplePlan string
123131
sampleUser string
124132
indexNames []string
125133
execCount int64
@@ -192,6 +200,8 @@ type StmtExecInfo struct {
192200
Digest string
193201
PrevSQL string
194202
PrevSQLDigest string
203+
PlanGenerator func() string
204+
PlanDigest string
195205
User string
196206
TotalLatency time.Duration
197207
ParseLatency time.Duration
@@ -231,6 +241,7 @@ func (ssMap *stmtSummaryByDigestMap) AddStatement(sei *StmtExecInfo) {
231241
schemaName: sei.SchemaName,
232242
digest: sei.Digest,
233243
prevDigest: sei.PrevSQLDigest,
244+
planDigest: sei.PlanDigest,
234245
}
235246

236247
// Enclose the block in a function to ensure the lock will always be released.
@@ -476,14 +487,6 @@ func (ssMap *stmtSummaryByDigestMap) historySize() int {
476487

477488
// newStmtSummaryByDigest creates a stmtSummaryByDigest from StmtExecInfo.
478489
func newStmtSummaryByDigest(sei *StmtExecInfo, beginTime int64, intervalSeconds int64, historySize int) *stmtSummaryByDigest {
479-
// Trim SQL to size MaxSQLLength.
480-
maxSQLLength := config.GetGlobalConfig().StmtSummary.MaxSQLLength
481-
normalizedSQL := sei.NormalizedSQL
482-
if len(normalizedSQL) > int(maxSQLLength) {
483-
// Make sure the memory of original `normalizedSQL` will be released.
484-
normalizedSQL = string([]byte(normalizedSQL[:maxSQLLength]))
485-
}
486-
487490
// Use "," to separate table names to support FIND_IN_SET.
488491
var buffer bytes.Buffer
489492
for i, value := range sei.StmtCtx.Tables {
@@ -499,8 +502,9 @@ func newStmtSummaryByDigest(sei *StmtExecInfo, beginTime int64, intervalSeconds
499502
ssbd := &stmtSummaryByDigest{
500503
schemaName: sei.SchemaName,
501504
digest: sei.Digest,
505+
planDigest: sei.PlanDigest,
502506
stmtType: strings.ToLower(sei.StmtCtx.StmtType),
503-
normalizedSQL: normalizedSQL,
507+
normalizedSQL: formatSQL(sei.NormalizedSQL),
504508
tableNames: tableNames,
505509
history: list.New(),
506510
}
@@ -589,8 +593,17 @@ func (ssbd *stmtSummaryByDigest) collectHistorySummaries(historySize int) []*stm
589593
}
590594

591595
func newStmtSummaryByDigestElement(sei *StmtExecInfo, beginTime int64, intervalSeconds int64) *stmtSummaryByDigestElement {
596+
// sampleSQL / sampleUser / samplePlan / prevSQL / indexNames store the values shown at the first time,
597+
// because it compacts performance to update every time.
592598
ssElement := &stmtSummaryByDigestElement{
593-
beginTime: beginTime,
599+
beginTime: beginTime,
600+
sampleSQL: formatSQL(sei.OriginalSQL),
601+
// PrevSQL is already truncated to cfg.Log.QueryLogMaxLen.
602+
prevSQL: sei.PrevSQL,
603+
// samplePlan needs to be decoded so it can't be truncated.
604+
samplePlan: sei.PlanGenerator(),
605+
sampleUser: sei.User,
606+
indexNames: sei.StmtCtx.IndexNames,
594607
minLatency: sei.TotalLatency,
595608
firstSeen: sei.StartTime,
596609
lastSeen: sei.StartTime,
@@ -619,28 +632,11 @@ func (ssElement *stmtSummaryByDigestElement) onExpire(intervalSeconds int64) {
619632
}
620633

621634
func (ssElement *stmtSummaryByDigestElement) add(sei *StmtExecInfo, intervalSeconds int64) {
622-
maxSQLLength := config.GetGlobalConfig().StmtSummary.MaxSQLLength
623-
sampleSQL := sei.OriginalSQL
624-
if len(sampleSQL) > int(maxSQLLength) {
625-
// Make sure the memory of original `sampleSQL` will be released.
626-
sampleSQL = string([]byte(sampleSQL[:maxSQLLength]))
627-
}
628-
prevSQL := sei.PrevSQL
629-
if len(prevSQL) > int(maxSQLLength) {
630-
prevSQL = string([]byte(prevSQL[:maxSQLLength]))
631-
}
632-
633635
ssElement.Lock()
634636
defer ssElement.Unlock()
635637

636638
// refreshInterval may change anytime, update endTime ASAP.
637639
ssElement.endTime = ssElement.beginTime + intervalSeconds
638-
if sei.User != "" {
639-
ssElement.sampleUser = sei.User
640-
}
641-
ssElement.sampleSQL = sampleSQL
642-
ssElement.prevSQL = prevSQL
643-
ssElement.indexNames = sei.StmtCtx.IndexNames
644640
ssElement.execCount++
645641

646642
// latency
@@ -768,6 +764,12 @@ func (ssElement *stmtSummaryByDigestElement) toDatum(ssbd *stmtSummaryByDigest)
768764
ssElement.Lock()
769765
defer ssElement.Unlock()
770766

767+
plan, err := plancodec.DecodePlan(ssElement.samplePlan)
768+
if err != nil {
769+
logutil.BgLogger().Error("decode plan in statement summary failed", zap.String("plan", ssElement.samplePlan), zap.Error(err))
770+
plan = ""
771+
}
772+
771773
// Actually, there's a small chance that endTime is out of date, but it's hard to keep it up to date all the time.
772774
return types.MakeDatums(
773775
types.Time{Time: types.FromGoTime(time.Unix(ssElement.beginTime, 0)), Type: mysql.TypeTimestamp},
@@ -834,9 +836,21 @@ func (ssElement *stmtSummaryByDigestElement) toDatum(ssbd *stmtSummaryByDigest)
834836
types.Time{Time: types.FromGoTime(ssElement.lastSeen), Type: mysql.TypeTimestamp},
835837
ssElement.sampleSQL,
836838
ssElement.prevSQL,
839+
ssbd.planDigest,
840+
plan,
837841
)
838842
}
839843

844+
// Truncate SQL to maxSQLLength.
845+
func formatSQL(sql string) string {
846+
maxSQLLength := config.GetGlobalConfig().StmtSummary.MaxSQLLength
847+
length := len(sql)
848+
if length > int(maxSQLLength) {
849+
sql = fmt.Sprintf("%.*s(len:%d)", maxSQLLength, sql, length)
850+
}
851+
return sql
852+
}
853+
840854
// Format the backoffType map to a string or nil.
841855
func formatBackoffTypes(backoffMap map[fmt.Stringer]int) interface{} {
842856
type backoffStat struct {

0 commit comments

Comments
 (0)