From d10e06f196c071dc26756804fa85c6eccc87f4ae Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 31 Jul 2023 11:27:50 +0800 Subject: [PATCH] feat(execution-plan): refine execution plan (#1561) --- pkg/apiserver/slowquery/model.go | 9 +- pkg/apiserver/slowquery/queries.go | 10 +- pkg/apiserver/statement/models.go | 3 +- pkg/apiserver/statement/service.go | 7 +- pkg/apiserver/utils/binary_plan.go | 16 +- .../src/client/api/models/slowquery-model.ts | 6 + .../src/client/api/models/statement-model.ts | 6 + .../tidb-dashboard-client/swagger/spec.json | 6 + .../src/apps/SlowQuery/pages/Detail/index.tsx | 76 +++--- .../src/apps/SlowQuery/translations/en.yaml | 3 +- .../src/apps/SlowQuery/translations/zh.yaml | 3 +- .../Statement/pages/Detail/PlanDetail.tsx | 79 +++--- .../src/apps/Statement/translations/en.yaml | 3 +- .../src/apps/Statement/translations/zh.yaml | 3 +- .../tidb-dashboard-lib/src/client/models.ts | 12 + .../src/components/BinaryPlanTable/index.tsx | 233 ++++++++++++++++++ .../sample-data/binary-plan.json | 189 ++++++++++++++ .../sample-data/detail-res.json | 72 ++++++ .../src/components/index.ts | 1 + 19 files changed, 649 insertions(+), 88 deletions(-) create mode 100644 ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/index.tsx create mode 100644 ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/sample-data/binary-plan.json create mode 100644 ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/sample-data/detail-res.json diff --git a/pkg/apiserver/slowquery/model.go b/pkg/apiserver/slowquery/model.go index 8239180352..2a25e86dd5 100644 --- a/pkg/apiserver/slowquery/model.go +++ b/pkg/apiserver/slowquery/model.go @@ -40,10 +40,11 @@ type Model struct { TxnStartTS string `gorm:"column:Txn_start_ts" json:"txn_start_ts"` // Detail - PrevStmt string `gorm:"column:Prev_stmt" json:"prev_stmt"` - Plan string `gorm:"column:Plan" json:"plan"` - BinaryPlan string `gorm:"column:Binary_plan" json:"binary_plan"` - Warnings datatypes.JSON `gorm:"column:Warnings" json:"warnings"` + PrevStmt string `gorm:"column:Prev_stmt" json:"prev_stmt"` + Plan string `gorm:"column:Plan" json:"plan"` // deprecated, replaced by BinaryPlanText + BinaryPlan string `gorm:"column:Binary_plan" json:"binary_plan"` + BinaryPlanText string `gorm:"column:Binary_plan_text" proj:"tidb_decode_binary_plan(Binary_plan)" json:"binary_plan_text"` + Warnings datatypes.JSON `gorm:"column:Warnings" json:"warnings"` // Basic IsInternal int `gorm:"column:Is_internal" json:"is_internal"` diff --git a/pkg/apiserver/slowquery/queries.go b/pkg/apiserver/slowquery/queries.go index d47709ef7a..678e39eb75 100644 --- a/pkg/apiserver/slowquery/queries.go +++ b/pkg/apiserver/slowquery/queries.go @@ -109,11 +109,19 @@ func QuerySlowLogList(req *GetListRequest, sysSchema *utils.SysSchema, db *gorm. func QuerySlowLogDetail(req *GetDetailRequest, db *gorm.DB) (*Model, error) { var result Model err := db. - Select("*, (UNIX_TIMESTAMP(Time) + 0E0) AS timestamp"). + Select("*, (UNIX_TIMESTAMP(Time) + 0E0) AS timestamp, tidb_decode_binary_plan(Binary_plan) AS Binary_plan_text"). Where("Digest = ?", req.Digest). Where("Time = FROM_UNIXTIME(?)", req.Timestamp). Where("Conn_id = ?", req.ConnectID). First(&result).Error + if err != nil { + err = db. + Select("*, (UNIX_TIMESTAMP(Time) + 0E0) AS timestamp"). + Where("Digest = ?", req.Digest). + Where("Time = FROM_UNIXTIME(?)", req.Timestamp). + Where("Conn_id = ?", req.ConnectID). + First(&result).Error + } if err != nil { return nil, err } diff --git a/pkg/apiserver/statement/models.go b/pkg/apiserver/statement/models.go index f3fe864932..72685e1864 100644 --- a/pkg/apiserver/statement/models.go +++ b/pkg/apiserver/statement/models.go @@ -80,9 +80,10 @@ type Model struct { AggTableNames string `json:"table_names" agg:"ANY_VALUE(table_names)"` AggIndexNames string `json:"index_names" agg:"ANY_VALUE(index_names)"` AggPlanCount int `json:"plan_count" agg:"COUNT(DISTINCT plan_digest)" related:"plan_digest"` - AggPlan string `json:"plan" agg:"ANY_VALUE(plan)"` + AggPlan string `json:"plan" agg:"ANY_VALUE(plan)"` // deprecated, replaced by BinaryPlanText AggPlanDigest string `json:"plan_digest" agg:"ANY_VALUE(plan_digest)"` AggBinaryPlan string `json:"binary_plan" agg:"ANY_VALUE(binary_plan)"` + AggBinaryPlanText string `json:"binary_plan_text" related:"binary_plan" agg:"tidb_decode_binary_plan(ANY_VALUE(binary_plan))"` AggPlanHint *string `json:"plan_hint" agg:"ANY_VALUE(plan_hint)"` // RocksDB AggMaxRocksdbDeleteSkippedCount uint `json:"max_rocksdb_delete_skipped_count" agg:"MAX(max_rocksdb_delete_skipped_count)"` diff --git a/pkg/apiserver/statement/service.go b/pkg/apiserver/statement/service.go index ddd4d626fe..96eae34b1e 100644 --- a/pkg/apiserver/statement/service.go +++ b/pkg/apiserver/statement/service.go @@ -231,11 +231,8 @@ func (s *Service) planDetailHandler(c *gin.Context) { } // get binary plan - result.AggBinaryPlan, err = utils.GenerateBinaryPlanJSON(result.AggBinaryPlan) - if err != nil { - rest.Error(c, err) - return - } + // may failed but it's ok + result.AggBinaryPlan, _ = utils.GenerateBinaryPlanJSON(result.AggBinaryPlan) c.JSON(http.StatusOK, result) } diff --git a/pkg/apiserver/utils/binary_plan.go b/pkg/apiserver/utils/binary_plan.go index 0ee4f1ffda..c813245d1d 100644 --- a/pkg/apiserver/utils/binary_plan.go +++ b/pkg/apiserver/utils/binary_plan.go @@ -505,9 +505,9 @@ func analyzeDurationNode(node *simplejson.Json, concurrency concurrency) (time.D // cop task if ts == "" { - ts = getCopTaskDuratuon(node, concurrency) + ts = getCopTaskDuration(node, concurrency) } else { - ts = getOperatorDuratuon(ts, concurrency) + ts = getOperatorDuration(ts, concurrency) } operator := getOperatorType(node) @@ -773,11 +773,11 @@ func getConcurrency(node *simplejson.Json, operator operator, concurrency concur } case Shuffle: - tmpSuffleConcurrencyStr := rootGroupInfo.GetIndex(i).Get("ShuffleConcurrency").MustString() - tmpSuffleConcurrency, _ := strconv.Atoi(tmpSuffleConcurrencyStr) + tmpShuffleConcurrencyStr := rootGroupInfo.GetIndex(i).Get("ShuffleConcurrency").MustString() + tmpShuffleConcurrency, _ := strconv.Atoi(tmpShuffleConcurrencyStr) - if tmpSuffleConcurrency > 0 { - concurrency.shuffleConcurrency = tmpSuffleConcurrency * concurrency.shuffleConcurrency + if tmpShuffleConcurrency > 0 { + concurrency.shuffleConcurrency = tmpShuffleConcurrency * concurrency.shuffleConcurrency } } @@ -792,7 +792,7 @@ func getConcurrency(node *simplejson.Json, operator operator, concurrency concur return concurrency } -func getCopTaskDuratuon(node *simplejson.Json, concurrency concurrency) string { +func getCopTaskDuration(node *simplejson.Json, concurrency concurrency) string { storeType := node.GetPath(StoreType).MustString() // task == 1 ts := node.GetPath(CopExecInfo, fmt.Sprintf("%s_task", storeType), "time").MustString() @@ -842,7 +842,7 @@ func getCopTaskDuratuon(node *simplejson.Json, concurrency concurrency) string { return ts } -func getOperatorDuratuon(ts string, concurrency concurrency) string { +func getOperatorDuration(ts string, concurrency concurrency) string { t, err := time.ParseDuration(ts) if err != nil { return "0s" diff --git a/ui/packages/tidb-dashboard-client/src/client/api/models/slowquery-model.ts b/ui/packages/tidb-dashboard-client/src/client/api/models/slowquery-model.ts index 63ba13610e..6850a8e07d 100644 --- a/ui/packages/tidb-dashboard-client/src/client/api/models/slowquery-model.ts +++ b/ui/packages/tidb-dashboard-client/src/client/api/models/slowquery-model.ts @@ -38,6 +38,12 @@ export interface SlowqueryModel { * @memberof SlowqueryModel */ 'binary_plan'?: string; + /** + * + * @type {string} + * @memberof SlowqueryModel + */ + 'binary_plan_text'?: string; /** * * @type {number} diff --git a/ui/packages/tidb-dashboard-client/src/client/api/models/statement-model.ts b/ui/packages/tidb-dashboard-client/src/client/api/models/statement-model.ts index 265390412f..4d3e11a111 100644 --- a/ui/packages/tidb-dashboard-client/src/client/api/models/statement-model.ts +++ b/ui/packages/tidb-dashboard-client/src/client/api/models/statement-model.ts @@ -194,6 +194,12 @@ export interface StatementModel { * @memberof StatementModel */ 'binary_plan'?: string; + /** + * + * @type {string} + * @memberof StatementModel + */ + 'binary_plan_text'?: string; /** * * @type {string} diff --git a/ui/packages/tidb-dashboard-client/swagger/spec.json b/ui/packages/tidb-dashboard-client/swagger/spec.json index 91469495bc..9cac01fa59 100644 --- a/ui/packages/tidb-dashboard-client/swagger/spec.json +++ b/ui/packages/tidb-dashboard-client/swagger/spec.json @@ -4906,6 +4906,9 @@ "binary_plan": { "type": "string" }, + "binary_plan_text": { + "type": "string" + }, "commit_backoff_time": { "type": "number" }, @@ -5310,6 +5313,9 @@ "binary_plan": { "type": "string" }, + "binary_plan_text": { + "type": "string" + }, "digest": { "type": "string" }, diff --git a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/pages/Detail/index.tsx b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/pages/Detail/index.tsx index ac18fec9b2..5f466972dd 100644 --- a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/pages/Detail/index.tsx +++ b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/pages/Detail/index.tsx @@ -9,6 +9,7 @@ import { buildQueryFn, parseQueryFn } from '@lib/utils/query' import formatSql from '@lib/utils/sqlFormatter' import { AnimatedSkeleton, + BinaryPlanTable, CopyLink, Descriptions, ErrorBar, @@ -72,8 +73,8 @@ function DetailPage() { setDetailExpand((prev) => ({ ...prev, prev_query: !prev.prev_query })) const toggleQuery = () => setDetailExpand((prev) => ({ ...prev, query: !prev.query })) - const togglePlan = () => - setDetailExpand((prev) => ({ ...prev, plan: !prev.plan })) + // const togglePlan = () => + // setDetailExpand((prev) => ({ ...prev, plan: !prev.plan })) const [isVpVisible, setIsVpVisable] = useState(false) const toggleVisualPlan = (action: 'open' | 'close') => { @@ -172,16 +173,50 @@ function DetailPage() { {t('slow_query.detail.plan.title')} telemetry.clickPlanTabs(key, data.digest!) } > - {binaryPlan && !binaryPlan.main.discardedDueToTooLong && ( + + + + {/* */} + + + } + > + {/* + */} +
{data.binary_plan_text ?? data.plan}
+
+
+
+ + {binaryPlan && !binaryPlan.discardedDueToTooLong && ( + + +
+ + )} + + {binaryPlan && !binaryPlan.discardedDueToTooLong && ( )} - - - - - - - - } - > - -
{data.plan}
-
-
-
-
)} diff --git a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/en.yaml b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/en.yaml index fe583c6ad4..1f906ebe9a 100644 --- a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/en.yaml +++ b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/en.yaml @@ -133,8 +133,9 @@ slow_query: previous_sql: Previous Query plan: title: Execution Plan - visual: Visual text: Text + table: Table + visual: Visual modal_title: Visual Plan tabs: basic: Basic diff --git a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/zh.yaml b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/zh.yaml index 5238404fe0..c8c8df0a28 100644 --- a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/zh.yaml +++ b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/zh.yaml @@ -135,8 +135,9 @@ slow_query: previous_sql: 上一条 SQL 查询 plan: title: 执行计划 - visual: 图形 text: 文本 + table: 表格 + visual: 图形 modal_title: 执行计划可视化 tabs: basic: 基本信息 diff --git a/ui/packages/tidb-dashboard-lib/src/apps/Statement/pages/Detail/PlanDetail.tsx b/ui/packages/tidb-dashboard-lib/src/apps/Statement/pages/Detail/PlanDetail.tsx index 17f9f0e834..79fe5b147b 100644 --- a/ui/packages/tidb-dashboard-lib/src/apps/Statement/pages/Detail/PlanDetail.tsx +++ b/ui/packages/tidb-dashboard-lib/src/apps/Statement/pages/Detail/PlanDetail.tsx @@ -3,6 +3,7 @@ import { Space, Tabs, Modal } from 'antd' import { useTranslation } from 'react-i18next' import { AnimatedSkeleton, + BinaryPlanTable, Card, CopyLink, Descriptions, @@ -84,8 +85,8 @@ function PlanDetail({ query }: IPlanDetailProps) { setDetailExpand((prev) => ({ ...prev, prev_query: !prev.prev_query })) const toggleQuery = () => setDetailExpand((prev) => ({ ...prev, query: !prev.query })) - const togglePlan = () => - setDetailExpand((prev) => ({ ...prev, plan: !prev.plan })) + // const togglePlan = () => + // setDetailExpand((prev) => ({ ...prev, plan: !prev.plan })) let titleKey if (query.allPlans === 1) { @@ -178,18 +179,56 @@ function PlanDetail({ query }: IPlanDetailProps) { telemetry.clickPlanTabs(key, data.digest!) } > + + + + {/* */} + + + } + > + {/* + */} +
{data.binary_plan_text ?? data.plan}
+
+
+
+ + {binaryPlan && !binaryPlan.discardedDueToTooLong && ( + + +
+ + )} + {binaryPlan && !binaryPlan.main.discardedDueToTooLong && ( )} - - - - - - - } - > - -
{data.plan}
-
-
-
-
)} diff --git a/ui/packages/tidb-dashboard-lib/src/apps/Statement/translations/en.yaml b/ui/packages/tidb-dashboard-lib/src/apps/Statement/translations/en.yaml index c4a4edc2e0..7a36d5336b 100755 --- a/ui/packages/tidb-dashboard-lib/src/apps/Statement/translations/en.yaml +++ b/ui/packages/tidb-dashboard-lib/src/apps/Statement/translations/en.yaml @@ -26,8 +26,9 @@ statement: some: 'Execution Detail of Selected {{n}} Plans' execution: title: Execution Plan - visual: Visual text: Text + table: Table + visual: Visual modal_title: Visual Plan tabs: basic: Basic diff --git a/ui/packages/tidb-dashboard-lib/src/apps/Statement/translations/zh.yaml b/ui/packages/tidb-dashboard-lib/src/apps/Statement/translations/zh.yaml index ba3c348b46..550cd4e05c 100755 --- a/ui/packages/tidb-dashboard-lib/src/apps/Statement/translations/zh.yaml +++ b/ui/packages/tidb-dashboard-lib/src/apps/Statement/translations/zh.yaml @@ -26,8 +26,9 @@ statement: some: '{{n}} 个执行计划的执行详情' execution: title: 执行计划 - visual: 图形 text: 文本 + table: 表格 + visual: 图形 modal_title: 执行计划可视化 tabs: basic: 基本信息 diff --git a/ui/packages/tidb-dashboard-lib/src/client/models.ts b/ui/packages/tidb-dashboard-lib/src/client/models.ts index 88e1d3d7a9..50a9961716 100644 --- a/ui/packages/tidb-dashboard-lib/src/client/models.ts +++ b/ui/packages/tidb-dashboard-lib/src/client/models.ts @@ -2176,6 +2176,12 @@ export interface SlowqueryModel { * @memberof SlowqueryModel */ 'binary_plan'?: string; + /** + * + * @type {string} + * @memberof SlowqueryModel + */ + 'binary_plan_text'?: string; /** * * @type {number} @@ -2928,6 +2934,12 @@ export interface StatementModel { * @memberof StatementModel */ 'binary_plan'?: string; + /** + * + * @type {string} + * @memberof StatementModel + */ + 'binary_plan_text'?: string; /** * * @type {string} diff --git a/ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/index.tsx b/ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/index.tsx new file mode 100644 index 0000000000..2f4d1c2c98 --- /dev/null +++ b/ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/index.tsx @@ -0,0 +1,233 @@ +import { IColumn } from 'office-ui-fabric-react' +import React, { useMemo } from 'react' +import CardTable from '../CardTable' +import { getValueFormat } from '@baurine/grafana-value-formats' +import { Tooltip } from 'antd' + +type BinaryPlanItem = { + name: string // id + + estRows: number + cost: number // estCost + actRows: number + + taskType: string // task + storeType: string // task + + accessObjects: any[] // access object + + rootBasicExecInfo: object // execution info + rootGroupExecInfo: any[] // execution info + copExecInfo: object // execution info + + operatorInfo: string // operator info + + memoryBytes: string // memory + diskBytes: string // disk + + children?: BinaryPlanItem[] + level: number +} + +type BinaryPlan = { + discardedDueToTooLong: boolean + withRuntimeStats: boolean + main: BinaryPlanItem +} + +type BinaryPlanTableProps = { + data: BinaryPlan +} + +function convertBinaryPlanToArray(binaryPlan: BinaryPlan): BinaryPlanItem[] { + const result: BinaryPlanItem[] = [] + const stack: BinaryPlanItem[] = [binaryPlan.main] + stack[0].level = 0 + while (stack.length > 0) { + const item = stack.pop()! + result.push(item) + + if (item.children !== undefined) { + for (let i = item.children.length - 1; i >= 0; i--) { + const child = item.children[i] + child.level = item.level + 1 + stack.push(child) + } + } + } + return result +} + +function getTableName(item: BinaryPlanItem): string { + let tableName = '' + if (!item?.accessObjects?.length) return '' + + const scanObject = item.accessObjects.find((obj) => + Object.keys(obj).includes('scanObject') + ) + + if (scanObject) { + tableName = scanObject['scanObject']['table'] + } + + return tableName +} + +function getExecutionInfo(item: BinaryPlanItem) { + let execInfo: string[] = [] + if (Object.keys(item.rootBasicExecInfo).length > 0) { + execInfo.push(JSON.stringify(item.rootBasicExecInfo)) + } + if (item.rootGroupExecInfo.length > 0) { + item.rootGroupExecInfo + .filter((i) => !!i) // filter out the NULL value + .forEach((info) => { + execInfo.push(JSON.stringify(info)) + }) + } + if (Object.keys(item.copExecInfo).length > 0) { + execInfo.push(JSON.stringify(item.copExecInfo)) + } + execInfo = execInfo.map((info) => + info.replaceAll('"', '').replaceAll(',', ', ') + ) + return execInfo +} + +function getMemorySize(item: BinaryPlanItem): string { + if (item.memoryBytes === 'N/A') { + return 'N/A' + } + return getValueFormat('bytes')(Number(item.memoryBytes), 1) +} + +function getDiskSize(item: BinaryPlanItem): string { + if (item.diskBytes === 'N/A') { + return 'N/A' + } + return getValueFormat('bytes')(Number(item.diskBytes), 1) +} + +export const BinaryPlanTable: React.FC = ({ data }) => { + const arr = useMemo(() => convertBinaryPlanToArray(data), [data]) + const columns: IColumn[] = useMemo(() => { + return [ + { + name: 'id', + key: 'name', + minWidth: 100, + maxWidth: 300, + onRender: (row: BinaryPlanItem) => { + return ( + + {row.level > 0 && '└─'} + {row.name} + + ) + } + }, + { + name: 'estRows', + key: 'estRows', + minWidth: 100, + maxWidth: 120, + onRender: (row: BinaryPlanItem) => { + return row.estRows.toFixed(2) + } + }, + { + name: 'estCost', + key: 'estCost', + minWidth: 100, + maxWidth: 120, + onRender: (row: BinaryPlanItem) => { + return (row.cost ?? 0).toFixed(2) + } + }, + { + name: 'actRows', + key: 'actRows', + minWidth: 100, + maxWidth: 120, + onRender: (row: BinaryPlanItem) => { + return row.actRows.toFixed(2) + } + }, + { + name: 'task', + key: 'taskType', + minWidth: 60, + maxWidth: 100, + onRender: (row: BinaryPlanItem) => { + let task = row.taskType + if (task !== 'root') { + task += `[${row.storeType}]` + } + return task + } + }, + { + name: 'access object', + key: 'accessObjects', + minWidth: 100, + maxWidth: 120, + onRender: (row: BinaryPlanItem) => { + const tableName = getTableName(row) + let content = !!tableName ? `table: ${tableName}` : '' + return content && {content} + } + }, + { + name: 'execution info', + key: 'rootGroupExecInfo', + minWidth: 100, + maxWidth: 300, + onRender: (row: BinaryPlanItem) => { + const execInfo = getExecutionInfo(row) + return ( + + {execInfo.map((info, idx) => ( +
{info}
+ ))} + + } + > + {execInfo.join(', ')} +
+ ) + } + }, + { + name: 'operator info', + key: 'operatorInfo', + minWidth: 100, + maxWidth: 300, + onRender: (row: BinaryPlanItem) => { + return {row.operatorInfo} + } + }, + { + name: 'memory', + key: 'memoryBytes', + minWidth: 60, + maxWidth: 100, + onRender: (row: BinaryPlanItem) => { + return getMemorySize(row) + } + }, + { + name: 'disk', + key: 'diskBytes', + minWidth: 60, + maxWidth: 100, + onRender: (row: BinaryPlanItem) => { + return getDiskSize(row) + } + } + ] + }, []) + + return +} diff --git a/ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/sample-data/binary-plan.json b/ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/sample-data/binary-plan.json new file mode 100644 index 0000000000..d07e2d00d5 --- /dev/null +++ b/ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/sample-data/binary-plan.json @@ -0,0 +1,189 @@ +{ + "discardedDueToTooLong": false, + "main": { + "accessObjects": [], + "actRows": 12, + "children": [ + { + "accessObjects": [], + "actRows": 12, + "children": [ + { + "accessObjects": [], + "actRows": 12, + "children": [ + { + "accessObjects": [], + "actRows": 69, + "children": [ + { + "accessObjects": [ + { + "scanObject": { + "database": "INFORMATION_SCHEMA", + "table": "CLUSTER_LOAD" + } + } + ], + "actRows": 1193, + "copExecInfo": {}, + "diagnosis": [], + "diskBytes": "N/A", + "duration": "1.51s", + "estRows": 10000, + "labels": [], + "memoryBytes": "N/A", + "name": "MemTableScan_12", + "rootBasicExecInfo": { + "loops": "3", + "time": "1.51s" + }, + "rootGroupExecInfo": [], + "storeType": "tidb", + "taskType": "root", + "__node_attrs": { + "id": "5", + "collapsed": false, + "collapsiable": false, + "isNodeDetailVisible": false, + "nodeFlexSize": { + "width": 280, + "height": 230 + } + } + } + ], + "copExecInfo": {}, + "cost": 499000, + "diagnosis": ["high_est_error"], + "diskBytes": "N/A", + "duration": "1.51s", + "estRows": 8000, + "labels": [], + "memoryBytes": "138400", + "name": "Selection_11", + "operatorInfo": "in(Column#3, \"memory\", \"cpu\")", + "rootBasicExecInfo": { + "loops": "2", + "time": "1.51s" + }, + "rootGroupExecInfo": [], + "storeType": "tidb", + "taskType": "root", + "__node_attrs": { + "id": "4", + "collapsed": false, + "collapsiable": true, + "isNodeDetailVisible": false, + "nodeFlexSize": { + "width": 280, + "height": 200 + } + } + } + ], + "copExecInfo": {}, + "cost": 1772970.6, + "diagnosis": [], + "diskBytes": "N/A", + "duration": "1.51s", + "estRows": 8000, + "labels": [], + "memoryBytes": "48580", + "name": "HashAgg_10", + "operatorInfo": "group by:Column#1, Column#2, Column#3, Column#4, funcs:json_objectagg(Column#5, Column#6)->Column#7, funcs:firstrow(Column#1)->Column#1, funcs:firstrow(Column#2)->Column#2, funcs:firstrow(Column#3)->Column#3, funcs:firstrow(Column#4)->Column#4", + "rootBasicExecInfo": { + "loops": "6", + "time": "1.51s" + }, + "rootGroupExecInfo": [ + { + "final_worker": { + "concurrency": "5", + "max": "1.513316486s", + "p95": "1.513316486s", + "task_num": "5", + "tot_exec": "223.043µs", + "tot_time": "7.566371614s", + "tot_wait": "7.566142195s", + "wall_time": "1.5133523s" + }, + "partial_worker": { + "concurrency": "5", + "max": "1.513153823s", + "p95": "1.513153823s", + "task_num": "1", + "tot_exec": "149.691µs", + "tot_time": "7.56536502s", + "tot_wait": "7.565194284s", + "wall_time": "1.513240352s" + } + } + ], + "storeType": "tidb", + "taskType": "root", + "__node_attrs": { + "id": "3", + "collapsed": false, + "collapsiable": true, + "isNodeDetailVisible": false, + "nodeFlexSize": { + "width": 280, + "height": 200 + } + } + } + ], + "copExecInfo": {}, + "cost": 1856802.6, + "diagnosis": [], + "diskBytes": "N/A", + "duration": "1.51s", + "estRows": 8000, + "labels": [], + "memoryBytes": "34596", + "name": "Projection_9", + "operatorInfo": "Column#1, Column#2, Column#3, Column#4, Column#7, field(lower(Column#1), tiflash, tikv, pd, tidb)->Column#8", + "rootBasicExecInfo": { + "loops": "6", + "time": "1.51s" + }, + "rootGroupExecInfo": [ + { + "Concurrency": "5" + } + ], + "storeType": "tidb", + "taskType": "root", + "__node_attrs": { + "id": "2", + "collapsed": false, + "collapsiable": true, + "isNodeDetailVisible": false, + "nodeFlexSize": { + "width": 280, + "height": 200 + } + } + } + ], + "copExecInfo": {}, + "cost": 7403943.686437106, + "diagnosis": [], + "diskBytes": "N/A", + "duration": "1.51s", + "estRows": 8000, + "labels": [], + "memoryBytes": "18792", + "name": "Sort_7", + "operatorInfo": "Column#8:desc, Column#2, Column#3, Column#4", + "rootBasicExecInfo": { + "loops": "2", + "time": "1.51s" + }, + "rootGroupExecInfo": [], + "storeType": "tidb", + "taskType": "root" + }, + "withRuntimeStats": true +} diff --git a/ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/sample-data/detail-res.json b/ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/sample-data/detail-res.json new file mode 100644 index 0000000000..52b281ffb6 --- /dev/null +++ b/ui/packages/tidb-dashboard-lib/src/components/BinaryPlanTable/sample-data/detail-res.json @@ -0,0 +1,72 @@ +{ + "digest": "877ddf60c6084ae30353cc484f375e5159c231f0d9363213d1d1cc2ffbd272ce", + "query": "SELECT *, FIELD(LOWER(A.TYPE), 'tiflash', 'tikv', 'pd', 'tidb') AS _ORDER FROM ( SELECT TYPE, INSTANCE, DEVICE_TYPE, DEVICE_NAME, JSON_OBJECTAGG(NAME, VALUE) AS JSON_VALUE FROM INFORMATION_SCHEMA.CLUSTER_LOAD WHERE DEVICE_TYPE IN (?,?) GROUP BY TYPE, INSTANCE, DEVICE_TYPE, DEVICE_NAME ) AS A ORDER BY _ORDER DESC, INSTANCE, DEVICE_TYPE, DEVICE_NAME [arguments: (\"memory\", \"cpu\")];", + "instance": "127.0.0.1:10080", + "db": "", + "connection_id": "5414958427354957035", + "success": 1, + "timestamp": 1690272708.823209, + "query_time": 1.51515176, + "parse_time": 0, + "compile_time": 0.00103503, + "rewrite_time": 0.000332248, + "preproc_subqueries_time": 0, + "optimize_time": 0.000354698, + "wait_ts": 0, + "cop_time": 0, + "lock_keys_time": 0, + "write_sql_response_total": 0.000068678, + "exec_retry_time": 0, + "memory_max": 220680, + "disk_max": 0, + "txn_start_ts": "0", + "prev_stmt": "", + "plan": "\tid \ttask\testRows\toperator info \tactRows\texecution info \tmemory \tdisk\n\tSort_7 \troot\t8000 \tColumn#8:desc, Column#2, Column#3, Column#4 \t12 \ttime:1.51s, loops:2 \t18.4 KB \t0 Bytes\n\t└─Projection_9 \troot\t8000 \tColumn#1, Column#2, Column#3, Column#4, Column#7, field(lower(Column#1), tiflash, tikv, pd, tidb)-\u003eColumn#8 \t12 \ttime:1.51s, loops:6, Concurrency:5 \t33.8 KB \tN/A\n\t └─HashAgg_10 \troot\t8000 \tgroup by:Column#1, Column#2, Column#3, Column#4, funcs:json_objectagg(Column#5, Column#6)-\u003eColumn#7, funcs:firstrow(Column#1)-\u003eColumn#1, funcs:firstrow(Column#2)-\u003eColumn#2, funcs:firstrow(Column#3)-\u003eColumn#3, funcs:firstrow(Column#4)-\u003eColumn#4\t12 \ttime:1.51s, loops:6, partial_worker:{wall_time:1.513240352s, concurrency:5, task_num:1, tot_wait:7.565194284s, tot_exec:149.691µs, tot_time:7.56536502s, max:1.513153823s, p95:1.513153823s}, final_worker:{wall_time:1.5133523s, concurrency:5, task_num:5, tot_wait:7.566142195s, tot_exec:223.043µs, tot_time:7.566371614s, max:1.513316486s, p95:1.513316486s}\t47.4 KB \tN/A\n\t └─Selection_11 \troot\t8000 \tin(Column#3, \"memory\", \"cpu\") \t69 \ttime:1.51s, loops:2 \t135.2 KB\tN/A\n\t └─MemTableScan_12\troot\t10000 \ttable:CLUSTER_LOAD \t1193 \ttime:1.51s, loops:3 \tN/A \tN/A", + "binary_plan": "{\"discardedDueToTooLong\":false,\"main\":{\"accessObjects\":[],\"actRows\":12,\"children\":[{\"accessObjects\":[],\"actRows\":12,\"children\":[{\"accessObjects\":[],\"actRows\":12,\"children\":[{\"accessObjects\":[],\"actRows\":69,\"children\":[{\"accessObjects\":[{\"scanObject\":{\"database\":\"INFORMATION_SCHEMA\",\"table\":\"CLUSTER_LOAD\"}}],\"actRows\":1193,\"copExecInfo\":{},\"diagnosis\":[],\"diskBytes\":\"N/A\",\"duration\":\"1.51s\",\"estRows\":10000,\"labels\":[],\"memoryBytes\":\"N/A\",\"name\":\"MemTableScan_12\",\"rootBasicExecInfo\":{\"loops\":\"3\",\"time\":\"1.51s\"},\"rootGroupExecInfo\":[],\"storeType\":\"tidb\",\"taskType\":\"root\"}],\"copExecInfo\":{},\"cost\":499000,\"diagnosis\":[\"high_est_error\"],\"diskBytes\":\"N/A\",\"duration\":\"1.51s\",\"estRows\":8000,\"labels\":[],\"memoryBytes\":\"138400\",\"name\":\"Selection_11\",\"operatorInfo\":\"in(Column#3, \\\"memory\\\", \\\"cpu\\\")\",\"rootBasicExecInfo\":{\"loops\":\"2\",\"time\":\"1.51s\"},\"rootGroupExecInfo\":[],\"storeType\":\"tidb\",\"taskType\":\"root\"}],\"copExecInfo\":{},\"cost\":1772970.6,\"diagnosis\":[],\"diskBytes\":\"N/A\",\"duration\":\"1.51s\",\"estRows\":8000,\"labels\":[],\"memoryBytes\":\"48580\",\"name\":\"HashAgg_10\",\"operatorInfo\":\"group by:Column#1, Column#2, Column#3, Column#4, funcs:json_objectagg(Column#5, Column#6)-\\u003eColumn#7, funcs:firstrow(Column#1)-\\u003eColumn#1, funcs:firstrow(Column#2)-\\u003eColumn#2, funcs:firstrow(Column#3)-\\u003eColumn#3, funcs:firstrow(Column#4)-\\u003eColumn#4\",\"rootBasicExecInfo\":{\"loops\":\"6\",\"time\":\"1.51s\"},\"rootGroupExecInfo\":[{\"final_worker\":{\"concurrency\":\"5\",\"max\":\"1.513316486s\",\"p95\":\"1.513316486s\",\"task_num\":\"5\",\"tot_exec\":\"223.043µs\",\"tot_time\":\"7.566371614s\",\"tot_wait\":\"7.566142195s\",\"wall_time\":\"1.5133523s\"},\"partial_worker\":{\"concurrency\":\"5\",\"max\":\"1.513153823s\",\"p95\":\"1.513153823s\",\"task_num\":\"1\",\"tot_exec\":\"149.691µs\",\"tot_time\":\"7.56536502s\",\"tot_wait\":\"7.565194284s\",\"wall_time\":\"1.513240352s\"}}],\"storeType\":\"tidb\",\"taskType\":\"root\"}],\"copExecInfo\":{},\"cost\":1856802.6,\"diagnosis\":[],\"diskBytes\":\"N/A\",\"duration\":\"1.51s\",\"estRows\":8000,\"labels\":[],\"memoryBytes\":\"34596\",\"name\":\"Projection_9\",\"operatorInfo\":\"Column#1, Column#2, Column#3, Column#4, Column#7, field(lower(Column#1), tiflash, tikv, pd, tidb)-\\u003eColumn#8\",\"rootBasicExecInfo\":{\"loops\":\"6\",\"time\":\"1.51s\"},\"rootGroupExecInfo\":[{\"Concurrency\":\"5\"}],\"storeType\":\"tidb\",\"taskType\":\"root\"}],\"copExecInfo\":{},\"cost\":7403943.686437106,\"diagnosis\":[],\"diskBytes\":\"N/A\",\"duration\":\"1.51s\",\"estRows\":8000,\"labels\":[],\"memoryBytes\":\"18792\",\"name\":\"Sort_7\",\"operatorInfo\":\"Column#8:desc, Column#2, Column#3, Column#4\",\"rootBasicExecInfo\":{\"loops\":\"2\",\"time\":\"1.51s\"},\"rootGroupExecInfo\":[],\"storeType\":\"tidb\",\"taskType\":\"root\"},\"withRuntimeStats\":true}", + "binary_plan_text": "\n| id | estRows | estCost | actRows | task | access object | execution info | operator info | memory | disk |\n| Sort_7 | 8000.00 | 7403943.69 | 12 | root | | time:1.51s, loops:2 | Column#8:desc, Column#2, Column#3, Column#4 | 18.4 KB | 0 Bytes |\n| └─Projection_9 | 8000.00 | 1856802.60 | 12 | root | | time:1.51s, loops:6, Concurrency:5 | Column#1, Column#2, Column#3, Column#4, Column#7, field(lower(Column#1), tiflash, tikv, pd, tidb)-\u003eColumn#8 | 33.8 KB | N/A |\n| └─HashAgg_10 | 8000.00 | 1772970.60 | 12 | root | | time:1.51s, loops:6, partial_worker:{wall_time:1.513240352s, concurrency:5, task_num:1, tot_wait:7.565194284s, tot_exec:149.691µs, tot_time:7.56536502s, max:1.513153823s, p95:1.513153823s}, final_worker:{wall_time:1.5133523s, concurrency:5, task_num:5, tot_wait:7.566142195s, tot_exec:223.043µs, tot_time:7.566371614s, max:1.513316486s, p95:1.513316486s} | group by:Column#1, Column#2, Column#3, Column#4, funcs:json_objectagg(Column#5, Column#6)-\u003eColumn#7, funcs:firstrow(Column#1)-\u003eColumn#1, funcs:firstrow(Column#2)-\u003eColumn#2, funcs:firstrow(Column#3)-\u003eColumn#3, funcs:firstrow(Column#4)-\u003eColumn#4 | 47.4 KB | N/A |\n| └─Selection_11 | 8000.00 | 499000.00 | 69 | root | | time:1.51s, loops:2 | in(Column#3, \"memory\", \"cpu\") | 135.2 KB | N/A |\n| └─MemTableScan_12 | 10000.00 | 0.00 | 1193 | root | table:CLUSTER_LOAD | time:1.51s, loops:3 | | N/A | N/A |\n", + "warnings": [ + { + "Level": "Warning", + "Message": "skip prepared plan-cache: PhysicalMemTable plan is un-cacheable" + } + ], + "is_internal": 0, + "index_names": "", + "stats": "", + "backoff_types": "", + "prepared": 1, + "plan_from_cache": 0, + "plan_from_binding": 0, + "user": "root", + "host": "127.0.0.1", + "process_time": 0, + "wait_time": 0, + "backoff_time": 0, + "get_commit_ts_time": 0, + "local_latch_wait_time": 0, + "resolve_lock_time": 0, + "prewrite_time": 0, + "wait_prewrite_binlog_time": 0, + "commit_time": 0, + "commit_backoff_time": 0, + "cop_proc_avg": 0, + "cop_proc_p90": 0, + "cop_proc_max": 0, + "cop_wait_avg": 0, + "cop_wait_p90": 0, + "cop_wait_max": 0, + "write_keys": 0, + "write_size": 0, + "prewrite_region": 0, + "txn_retry": 0, + "request_count": 0, + "process_keys": 0, + "total_keys": 0, + "cop_proc_addr": "", + "cop_wait_addr": "", + "rocksdb_delete_skipped_count": 0, + "rocksdb_key_skipped_count": 0, + "rocksdb_block_cache_hit_count": 0, + "rocksdb_block_read_count": 0, + "rocksdb_block_read_byte": 0 +} diff --git a/ui/packages/tidb-dashboard-lib/src/components/index.ts b/ui/packages/tidb-dashboard-lib/src/components/index.ts index ec1374eac2..d2187e42a6 100644 --- a/ui/packages/tidb-dashboard-lib/src/components/index.ts +++ b/ui/packages/tidb-dashboard-lib/src/components/index.ts @@ -55,6 +55,7 @@ export * from './DrawerFooter' export { default as DrawerFooter } from './DrawerFooter' export * from './VisualPlan' +export * from './BinaryPlanTable' export { default as LanguageDropdown } from './LanguageDropdown' export { default as ParamsPageWrapper } from './ParamsPageWrapper'