planner: show operator cost formulas under 'explain format=true_card_cost' (#38405)

ref pingcap/tidb#36243
This commit is contained in:
Yuanjia Zhang
2022-10-11 18:53:50 +08:00
committed by GitHub
parent a32941cedd
commit f343ffef6d
4 changed files with 74 additions and 28 deletions

View File

@ -666,12 +666,14 @@ func (e *Explain) prepareSchema() error {
switch {
case (format == types.ExplainFormatROW || format == types.ExplainFormatBrief) && (!e.Analyze && e.RuntimeStatsColl == nil):
fieldNames = []string{"id", "estRows", "task", "access object", "operator info"}
case format == types.ExplainFormatVerbose || format == types.ExplainFormatTrueCardCost:
case format == types.ExplainFormatVerbose:
if e.Analyze || e.RuntimeStatsColl != nil {
fieldNames = []string{"id", "estRows", "estCost", "actRows", "task", "access object", "execution info", "operator info", "memory", "disk"}
} else {
fieldNames = []string{"id", "estRows", "estCost", "task", "access object", "operator info"}
}
case format == types.ExplainFormatTrueCardCost:
fieldNames = []string{"id", "estRows", "estCost", "costFormula", "actRows", "task", "access object", "execution info", "operator info", "memory", "disk"}
case (format == types.ExplainFormatROW || format == types.ExplainFormatBrief) && (e.Analyze || e.RuntimeStatsColl != nil):
fieldNames = []string{"id", "estRows", "actRows", "task", "access object", "execution info", "operator info", "memory", "disk"}
case format == types.ExplainFormatDOT:
@ -850,7 +852,7 @@ func (e *Explain) prepareOperatorInfo(p Plan, taskType, id string) {
return
}
estRows, estCost, accessObject, operatorInfo := e.getOperatorInfo(p, id)
estRows, estCost, costFormula, accessObject, operatorInfo := e.getOperatorInfo(p, id)
var row []string
if e.Analyze || e.RuntimeStatsColl != nil {
@ -858,6 +860,9 @@ func (e *Explain) prepareOperatorInfo(p Plan, taskType, id string) {
if strings.ToLower(e.Format) == types.ExplainFormatVerbose || strings.ToLower(e.Format) == types.ExplainFormatTrueCardCost {
row = append(row, estCost)
}
if strings.ToLower(e.Format) == types.ExplainFormatTrueCardCost {
row = append(row, costFormula)
}
actRows, analyzeInfo, memoryInfo, diskInfo := getRuntimeInfoStr(e.ctx, p, e.RuntimeStatsColl)
row = append(row, actRows, taskType, accessObject, analyzeInfo, operatorInfo, memoryInfo, diskInfo)
} else {
@ -870,14 +875,14 @@ func (e *Explain) prepareOperatorInfo(p Plan, taskType, id string) {
e.Rows = append(e.Rows, row)
}
func (e *Explain) getOperatorInfo(p Plan, id string) (string, string, string, string) {
func (e *Explain) getOperatorInfo(p Plan, id string) (string, string, string, string, string) {
// For `explain for connection` statement, `e.ExplainRows` will be set.
for _, row := range e.ExplainRows {
if len(row) < 5 {
panic("should never happen")
}
if row[0] == id {
return row[1], "N/A", row[3], row[4]
return row[1], "N/A", "N/A", row[3], row[4]
}
}
estRows := "N/A"
@ -885,9 +890,16 @@ func (e *Explain) getOperatorInfo(p Plan, id string) (string, string, string, st
estRows = strconv.FormatFloat(si.RowCount, 'f', 2, 64)
}
estCost := "N/A"
costFormula := "N/A"
if pp, ok := p.(PhysicalPlan); ok {
planCost, _ := getPlanCost(pp, property.RootTaskType, NewDefaultPlanCostOption())
estCost = strconv.FormatFloat(planCost, 'f', 2, 64)
if e.ctx != nil && e.ctx.GetSessionVars().CostModelVersion == modelVer2 {
costVer2, _ := pp.getPlanCostVer2(property.RootTaskType, NewDefaultPlanCostOption())
estCost = strconv.FormatFloat(costVer2.cost, 'f', 2, 64)
costFormula = costVer2.formula
} else {
planCost, _ := getPlanCost(pp, property.RootTaskType, NewDefaultPlanCostOption())
estCost = strconv.FormatFloat(planCost, 'f', 2, 64)
}
}
var accessObject, operatorInfo string
if plan, ok := p.(dataAccesser); ok {
@ -899,7 +911,7 @@ func (e *Explain) getOperatorInfo(p Plan, id string) (string, string, string, st
}
operatorInfo = p.ExplainInfo()
}
return estRows, estCost, accessObject, operatorInfo
return estRows, estCost, costFormula, accessObject, operatorInfo
}
// BinaryPlanStrFromFlatPlan generates the compressed and encoded binary plan from a FlatPhysicalPlan.

View File

@ -54,7 +54,7 @@ func (p *basePhysicalPlan) getPlanCostVer2(taskType property.TaskType, option *P
p.planCostVer2 = sumCostVer2(childCosts...)
}
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -76,7 +76,7 @@ func (p *PhysicalSelection) getPlanCostVer2(taskType property.TaskType, option *
p.planCostVer2 = sumCostVer2(filterCost, childCost)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -100,7 +100,7 @@ func (p *PhysicalProjection) getPlanCostVer2(taskType property.TaskType, option
p.planCostVer2 = sumCostVer2(childCost, divCostVer2(projCost, concurrency))
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -117,7 +117,7 @@ func (p *PhysicalIndexScan) getPlanCostVer2(taskType property.TaskType, option *
p.planCostVer2 = scanCostVer2(option, rows, rowSize, scanFactor)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -140,7 +140,7 @@ func (p *PhysicalTableScan) getPlanCostVer2(taskType property.TaskType, option *
}
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -168,7 +168,7 @@ func (p *PhysicalIndexReader) getPlanCostVer2(taskType property.TaskType, option
p.planCostVer2 = divCostVer2(sumCostVer2(childCost, netCost, seekCost), concurrency)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -203,7 +203,7 @@ func (p *PhysicalTableReader) getPlanCostVer2(taskType property.TaskType, option
!hasCostFlag(option.CostFlag, CostFlagRecalculate) { // show the real cost in explain-statements
p.planCostVer2 = divCostVer2(p.planCostVer2, 1000000000)
}
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -259,7 +259,7 @@ func (p *PhysicalIndexLookUpReader) getPlanCostVer2(taskType property.TaskType,
p.planCostVer2 = sumCostVer2(indexSideCost, divCostVer2(sumCostVer2(tableSideCost, doubleReadCost), doubleReadConcurrency))
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -307,7 +307,7 @@ func (p *PhysicalIndexMergeReader) getPlanCostVer2(taskType property.TaskType, o
p.planCostVer2 = sumCostVer2(tableSideCost, sumIndexSideCost)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -362,7 +362,7 @@ func (p *PhysicalSort) getPlanCostVer2(taskType property.TaskType, option *PlanC
p.planCostVer2 = sumCostVer2(childCost, sortCPUCost, sortMemCost, sortDiskCost)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -394,7 +394,7 @@ func (p *PhysicalTopN) getPlanCostVer2(taskType property.TaskType, option *PlanC
p.planCostVer2 = sumCostVer2(childCost, topNCPUCost, topNMemCost)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -417,7 +417,7 @@ func (p *PhysicalStreamAgg) getPlanCostVer2(taskType property.TaskType, option *
p.planCostVer2 = sumCostVer2(childCost, aggCost, groupCost)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -446,7 +446,7 @@ func (p *PhysicalHashAgg) getPlanCostVer2(taskType property.TaskType, option *Pl
p.planCostVer2 = sumCostVer2(childCost, divCostVer2(sumCostVer2(aggCost, groupCost, hashBuildCost, hashProbeCost), concurrency))
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -476,7 +476,7 @@ func (p *PhysicalMergeJoin) getPlanCostVer2(taskType property.TaskType, option *
p.planCostVer2 = sumCostVer2(leftChildCost, rightChildCost, filterCost, groupCost)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -520,7 +520,7 @@ func (p *PhysicalHashJoin) getPlanCostVer2(taskType property.TaskType, option *P
p.planCostVer2 = sumCostVer2(buildChildCost, probeChildCost, buildHashCost, buildFilterCost,
divCostVer2(sumCostVer2(probeFilterCost, probeHashCost), concurrency))
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -560,7 +560,7 @@ func (p *PhysicalIndexJoin) getPlanCostVer2(taskType property.TaskType, option *
p.planCostVer2 = sumCostVer2(buildChildCost, buildFilterCost, divCostVer2(sumCostVer2(probeCost, probeFilterCost), probeConcurrency))
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
func (p *PhysicalIndexHashJoin) getPlanCostVer2(taskType property.TaskType, option *PlanCostOption) (costVer2, error) {
@ -601,7 +601,7 @@ func (p *PhysicalApply) getPlanCostVer2(taskType property.TaskType, option *Plan
p.planCostVer2 = sumCostVer2(buildChildCost, buildFilterCost, probeCost, probeFilterCost)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 calculates the cost of the plan if it has not been calculated yet and returns the cost.
@ -622,7 +622,7 @@ func (p *PhysicalUnionAll) getPlanCostVer2(taskType property.TaskType, option *P
}
p.planCostVer2 = divCostVer2(sumCostVer2(childCosts...), concurrency)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -644,7 +644,7 @@ func (p *PhysicalExchangeReceiver) getPlanCostVer2(taskType property.TaskType, o
p.planCostVer2 = sumCostVer2(childCost, netCost)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -668,7 +668,7 @@ func (p *PointGetPlan) getPlanCostVer2(taskType property.TaskType, option *PlanC
p.planCostVer2 = sumCostVer2(netCost, seekCost)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
// getPlanCostVer2 returns the plan-cost of this sub-plan, which is:
@ -693,7 +693,7 @@ func (p *BatchPointGetPlan) getPlanCostVer2(taskType property.TaskType, option *
p.planCostVer2 = sumCostVer2(netCost, seekCost)
p.planCostInit = true
return p.planCostVer2, nil
return p.planCostVer2.label(p), nil
}
func scanCostVer2(option *PlanCostOption, rows, rowSize float64, scanFactor costVer2Factor) costVer2 {
@ -929,6 +929,14 @@ type costVer2 struct {
formula string // It used to trace the cost calculation.
}
func (c costVer2) label(p PhysicalPlan) costVer2 {
if !c.trace {
return c
}
c.formula = p.ExplainID().String()
return c
}
func traceCost(option *PlanCostOption) bool {
if option != nil && hasCostFlag(option.CostFlag, CostFlagTrace) {
return true

View File

@ -129,6 +129,28 @@ func TestCostModelVer2(t *testing.T) {
}
}
func TestCostModelShowFormula(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec(`create table t (a int)`)
tk.MustExec("insert into t values (1), (2), (3)")
tk.MustExec("set @@tidb_cost_model_version=2")
tk.MustExecToErr("explain format='true_card_cost' select * from t") // 'true_card_cost' must work with 'explain analyze'
plan := tk.MustQuery("explain analyze format='true_card_cost' select * from t where a<3").Rows()
actual := make([][]interface{}, 0, len(plan))
for _, row := range plan {
actual = append(actual, []interface{}{row[0], row[3]}) // id,costFormula
fmt.Println(actual)
}
require.Equal(t, actual, [][]interface{}{
{"TableReader_7", "((Selection_6) + (net(2*rowsize(16)*tidb_kv_net_factor(8))) + (seek(tasks(20)*tidb_request_factor(9.5e+06))))/15"},
{"└─Selection_6", "(cpu(3*filters(1)*tikv_cpu_factor(30))) + (TableFullScan_5)"},
{" └─TableFullScan_5", "scan(3*logrowsize(29)*tikv_scan_factor(100))"},
})
}
func TestCostModelTraceVer2(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)

View File

@ -4641,6 +4641,10 @@ func (b *PlanBuilder) buildTrace(trace *ast.TraceStmt) (Plan, error) {
}
func (b *PlanBuilder) buildExplainPlan(targetPlan Plan, format string, explainRows [][]string, analyze bool, execStmt ast.StmtNode, runtimeStats *execdetails.RuntimeStatsColl) (Plan, error) {
if strings.ToLower(format) == types.ExplainFormatTrueCardCost && !analyze {
return nil, errors.Errorf("'explain format=%v' cannot work without 'analyze', please use 'explain analyze format=%v'", format, format)
}
p := &Explain{
TargetPlan: targetPlan,
Format: format,