planner: show operator cost formulas under 'explain format=true_card_cost' (#38405)
ref pingcap/tidb#36243
This commit is contained in:
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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,
|
||||
|
||||
Reference in New Issue
Block a user