From 081920da935e04967df1c408841e013d010eb1b3 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Wed, 26 Sep 2018 19:28:53 +0800 Subject: [PATCH] planner,executor: support `admin show slow` command (#7785) --- executor/adapter.go | 6 ++++ executor/builder.go | 10 ++++++ executor/executor.go | 63 ++++++++++++++++++++++++++++++++++++ executor/show_test.go | 15 ++++++++- planner/core/common_plans.go | 7 ++++ planner/core/planbuilder.go | 26 +++++++++++++++ 6 files changed, 126 insertions(+), 1 deletion(-) diff --git a/executor/adapter.go b/executor/adapter.go index e5c4d72297af9..8bd9cf3242011 100644 --- a/executor/adapter.go +++ b/executor/adapter.go @@ -381,6 +381,12 @@ func (a *ExecStmt) logSlowQuery(txnTS uint64, succ bool) { if user != nil { userString = user.String() } + if len(tableIDs) > 10 { + tableIDs = tableIDs[10 : len(tableIDs)-1] // Remove "table_ids:" and the last "," + } + if len(indexIDs) > 10 { + indexIDs = indexIDs[10 : len(indexIDs)-1] // Remove "index_ids:" and the last "," + } domain.GetDomain(a.Ctx).LogSlowQuery(&domain.SlowQueryInfo{ SQL: sql, Start: a.startTime, diff --git a/executor/builder.go b/executor/builder.go index de7c4056c6a91..8ac333c223372 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -116,6 +116,8 @@ func (b *executorBuilder) build(p plannercore.Plan) Executor { return b.buildShowDDLJobs(v) case *plannercore.ShowDDLJobQueries: return b.buildShowDDLJobQueries(v) + case *plannercore.ShowSlow: + return b.buildShowSlow(v) case *plannercore.Show: return b.buildShow(v) case *plannercore.Simple: @@ -226,6 +228,14 @@ func (b *executorBuilder) buildShowDDLJobQueries(v *plannercore.ShowDDLJobQuerie return e } +func (b *executorBuilder) buildShowSlow(v *plannercore.ShowSlow) Executor { + e := &ShowSlowExec{ + baseExecutor: newBaseExecutor(b.ctx, v.Schema(), v.ExplainID()), + ShowSlow: v.ShowSlow, + } + return e +} + func (b *executorBuilder) buildCheckIndex(v *plannercore.CheckIndex) Executor { readerExec, err := buildNoRangeIndexLookUpReader(b, v.IndexLookUpReader) if err != nil { diff --git a/executor/executor.go b/executor/executor.go index 1ec327617dfc1..9b14a7fee8828 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -21,10 +21,12 @@ import ( "github.com/cznic/mathutil" "github.com/pingcap/tidb/ast" + "github.com/pingcap/tidb/domain" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/model" + "github.com/pingcap/tidb/mysql" plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/table" @@ -472,6 +474,67 @@ func (e *CheckIndexExec) Next(ctx context.Context, chk *chunk.Chunk) error { return nil } +// ShowSlowExec represents the executor of showing the slow queries. +// It is build from the "admin show slow" statement: +// admin show slow top [internal | all] N +// admin show slow recent N +type ShowSlowExec struct { + baseExecutor + + ShowSlow *ast.ShowSlow + result []*domain.SlowQueryInfo + cursor int +} + +// Open implements the Executor Open interface. +func (e *ShowSlowExec) Open(ctx context.Context) error { + if err := e.baseExecutor.Open(ctx); err != nil { + return errors.Trace(err) + } + + dom := domain.GetDomain(e.ctx) + e.result = dom.ShowSlowQuery(e.ShowSlow) + return nil +} + +// Next implements the Executor Next interface. +func (e *ShowSlowExec) Next(ctx context.Context, chk *chunk.Chunk) error { + chk.Reset() + if e.cursor >= len(e.result) { + return nil + } + + for e.cursor < len(e.result) && chk.NumRows() < e.maxChunkSize { + slow := e.result[e.cursor] + chk.AppendString(0, slow.SQL) + chk.AppendTime(1, types.Time{ + Time: types.FromGoTime(slow.Start), + Type: mysql.TypeTimestamp, + Fsp: types.MaxFsp, + }) + chk.AppendDuration(2, types.Duration{Duration: slow.Duration, Fsp: types.MaxFsp}) + chk.AppendString(3, slow.Detail.String()) + if slow.Succ { + chk.AppendInt64(4, 1) + } else { + chk.AppendInt64(4, 0) + } + chk.AppendUint64(5, slow.ConnID) + chk.AppendUint64(6, slow.TxnTS) + chk.AppendString(7, slow.User) + chk.AppendString(8, slow.DB) + chk.AppendString(9, slow.TableIDs) + chk.AppendString(10, slow.IndexIDs) + if slow.Internal { + chk.AppendInt64(11, 0) + } else { + chk.AppendInt64(11, 1) + } + e.cursor++ + } + return nil +} + // SelectLockExec represents a select lock executor. // It is built from the "SELECT .. FOR UPDATE" or the "SELECT .. LOCK IN SHARE MODE" statement. // For "SELECT .. FOR UPDATE" statement, it locks every row key from source Executor. diff --git a/executor/show_test.go b/executor/show_test.go index 80a71938847da..2f61d67a6a371 100644 --- a/executor/show_test.go +++ b/executor/show_test.go @@ -646,7 +646,7 @@ func (s *testSuite) TestShowTableStatus(c *C) { partition by range(a) ( partition p0 values less than (10), partition p1 values less than (20), - partition p2 values less than (maxvalue) + partition p2 values less than (maxvalue) );`) rs, err = tk.Exec("show table status from test like 'tp';") c.Assert(errors.ErrorStack(err), Equals, "") @@ -654,3 +654,16 @@ func (s *testSuite) TestShowTableStatus(c *C) { c.Assert(errors.ErrorStack(err), Equals, "") c.Assert(rows[0].GetString(16), Equals, "partitioned") } + +func (s *testSuite) TestShowSlow(c *C) { + tk := testkit.NewTestKit(c, s.store) + // The test result is volatile, because + // 1. Slow queries is stored in domain, which may be affected by other tests. + // 2. Collecting slow queries is a asynchronous process, check immediately may not get the expected result. + // 3. Make slow query like "select sleep(1)" would slow the CI. + // So, we just cover the code but do not check the result. + tk.MustQuery(`admin show slow recent 3`) + tk.MustQuery(`admin show slow top 3`) + tk.MustQuery(`admin show slow top internal 3`) + tk.MustQuery(`admin show slow top all 3`) +} diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index 9e5f8ff82f4de..f06459ffbbf7b 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -44,6 +44,13 @@ type ShowDDLJobs struct { JobNumber int64 } +// ShowSlow is for showing slow queries. +type ShowSlow struct { + baseSchemaProducer + + *ast.ShowSlow +} + // ShowDDLJobQueries is for showing DDL job queries sql. type ShowDDLJobQueries struct { baseSchemaProducer diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 748ec72aaaa5f..33c48c11abe8f 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -501,6 +501,10 @@ func (b *planBuilder) buildAdmin(as *ast.AdminStmt) (Plan, error) { p := &ShowDDLJobQueries{JobIDs: as.JobIDs} p.SetSchema(buildShowDDLJobQueriesFields()) ret = p + case ast.AdminShowSlow: + p := &ShowSlow{ShowSlow: as.ShowSlow} + p.SetSchema(buildShowSlowSchema()) + ret = p default: return nil, ErrUnsupportedType.GenWithStack("Unsupported ast.AdminStmt(%T) for buildAdmin", as) } @@ -746,6 +750,28 @@ func buildShowDDLJobQueriesFields() *expression.Schema { return schema } +func buildShowSlowSchema() *expression.Schema { + longlongSize, _ := mysql.GetDefaultFieldLengthAndDecimal(mysql.TypeLonglong) + tinySize, _ := mysql.GetDefaultFieldLengthAndDecimal(mysql.TypeTiny) + timestampSize, _ := mysql.GetDefaultFieldLengthAndDecimal(mysql.TypeTimestamp) + durationSize, _ := mysql.GetDefaultFieldLengthAndDecimal(mysql.TypeDuration) + + schema := expression.NewSchema(make([]*expression.Column, 0, 11)...) + schema.Append(buildColumn("", "SQL", mysql.TypeVarchar, 4096)) + schema.Append(buildColumn("", "START", mysql.TypeTimestamp, timestampSize)) + schema.Append(buildColumn("", "DURATION", mysql.TypeDuration, durationSize)) + schema.Append(buildColumn("", "DETAILS", mysql.TypeVarchar, 256)) + schema.Append(buildColumn("", "SUCC", mysql.TypeTiny, tinySize)) + schema.Append(buildColumn("", "CONN_ID", mysql.TypeLonglong, longlongSize)) + schema.Append(buildColumn("", "TRANSACTION_TS", mysql.TypeLonglong, longlongSize)) + schema.Append(buildColumn("", "USER", mysql.TypeVarchar, 32)) + schema.Append(buildColumn("", "DB", mysql.TypeVarchar, 64)) + schema.Append(buildColumn("", "TABLE_IDS", mysql.TypeVarchar, 256)) + schema.Append(buildColumn("", "INDEX_IDS", mysql.TypeVarchar, 256)) + schema.Append(buildColumn("", "INTERNAL", mysql.TypeTiny, tinySize)) + return schema +} + func buildCancelDDLJobsFields() *expression.Schema { schema := expression.NewSchema(make([]*expression.Column, 0, 2)...) schema.Append(buildColumn("", "JOB_ID", mysql.TypeVarchar, 64))