Skip to content

Commit

Permalink
planner: support cost detail framework (#36641)
Browse files Browse the repository at this point in the history
ref #29661
  • Loading branch information
Yisaer authored Aug 3, 2022
1 parent 3847d9e commit 8e9e839
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 74 deletions.
7 changes: 4 additions & 3 deletions planner/core/common_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,8 @@ func (e *Explain) RenderResult() error {
if e.Analyze && strings.ToLower(e.Format) == types.ExplainFormatTrueCardCost {
pp, ok := e.TargetPlan.(PhysicalPlan)
if ok {
if _, err := pp.GetPlanCost(property.RootTaskType, CostFlagRecalculate|CostFlagUseTrueCardinality); err != nil {
if _, err := pp.GetPlanCost(property.RootTaskType,
NewDefaultPlanCostOption().WithCostFlag(CostFlagRecalculate|CostFlagUseTrueCardinality)); err != nil {
return err
}
} else {
Expand Down Expand Up @@ -843,7 +844,7 @@ func (e *Explain) getOperatorInfo(p Plan, id string) (string, string, string, st
estCost := "N/A"
if pp, ok := p.(PhysicalPlan); ok {
if p.SCtx().GetSessionVars().EnableNewCostInterface {
planCost, _ := pp.GetPlanCost(property.RootTaskType, 0)
planCost, _ := pp.GetPlanCost(property.RootTaskType, NewDefaultPlanCostOption())
estCost = strconv.FormatFloat(planCost, 'f', 2, 64)
} else {
estCost = strconv.FormatFloat(pp.Cost(), 'f', 2, 64)
Expand Down Expand Up @@ -953,7 +954,7 @@ func binaryOpFromFlatOp(explainCtx sessionctx.Context, op *FlatOperator, out *ti
if op.IsPhysicalPlan {
p := op.Origin.(PhysicalPlan)
if p.SCtx().GetSessionVars().EnableNewCostInterface {
out.Cost, _ = p.GetPlanCost(property.RootTaskType, 0)
out.Cost, _ = p.GetPlanCost(property.RootTaskType, NewDefaultPlanCostOption())
} else {
out.Cost = p.Cost()
}
Expand Down
38 changes: 23 additions & 15 deletions planner/core/find_best_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ func (p *baseLogicalPlan) rebuildChildTasks(childTasks *[]task, pp PhysicalPlan,
return nil
}

func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPlan, prop *property.PhysicalProperty, addEnforcer bool, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (task, int64, error) {
func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPlan,
prop *property.PhysicalProperty, addEnforcer bool, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (task, int64, error) {
var bestTask task = invalidTask
var curCntPlan, cntPlan int64
childTasks := make([]task, 0, len(p.children))
Expand Down Expand Up @@ -278,7 +279,7 @@ func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPl
}
opt.appendCandidate(p, curTask.plan(), prop)
// Get the most efficient one.
if curIsBetter, err := compareTaskCost(p.ctx, curTask, bestTask); err != nil {
if curIsBetter, err := compareTaskCost(p.ctx, curTask, bestTask, opt); err != nil {
return nil, 0, err
} else if curIsBetter {
bestTask = curTask
Expand All @@ -288,12 +289,12 @@ func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPl
}

// compareTaskCost compares cost of curTask and bestTask and returns whether curTask's cost is smaller than bestTask's.
func compareTaskCost(_ sessionctx.Context, curTask, bestTask task) (curIsBetter bool, err error) {
curCost, curInvalid, err := getTaskPlanCost(curTask)
func compareTaskCost(_ sessionctx.Context, curTask, bestTask task, op *physicalOptimizeOp) (curIsBetter bool, err error) {
curCost, curInvalid, err := getTaskPlanCost(curTask, op)
if err != nil {
return false, err
}
bestCost, bestInvalid, err := getTaskPlanCost(bestTask)
bestCost, bestInvalid, err := getTaskPlanCost(bestTask, op)
if err != nil {
return false, err
}
Expand All @@ -309,7 +310,7 @@ func compareTaskCost(_ sessionctx.Context, curTask, bestTask task) (curIsBetter
// getTaskPlanCost returns the cost of this task.
// The new cost interface will be used if EnableNewCostInterface is true.
// The second returned value indicates whether this task is valid.
func getTaskPlanCost(t task) (float64, bool, error) {
func getTaskPlanCost(t task, op *physicalOptimizeOp) (float64, bool, error) {
if t.invalid() {
return math.MaxFloat64, true, nil
}
Expand All @@ -329,7 +330,7 @@ func getTaskPlanCost(t task) (float64, bool, error) {
default:
return 0, false, errors.New("unknown task type")
}
cost, err := t.plan().GetPlanCost(taskType, 0)
cost, err := t.plan().GetPlanCost(taskType, NewDefaultPlanCostOption().WithOptimizeTracer(op))
return cost, false, err
}

Expand Down Expand Up @@ -359,6 +360,13 @@ func (op *physicalOptimizeOp) appendCandidate(lp LogicalPlan, pp PhysicalPlan, p
pp.appendChildCandidate(op)
}

func (op *physicalOptimizeOp) appendPlanCostDetail(detail *tracing.PhysicalPlanCostDetail) {
if op == nil || op.tracer == nil {
return
}
op.tracer.PhysicalPlanCostDetails[detail.GetPlanID()] = detail
}

// findBestTask implements LogicalPlan interface.
func (p *baseLogicalPlan) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (bestTask task, cntPlan int64, err error) {
// If p is an inner plan in an IndexJoin, the IndexJoin will generate an inner plan by itself,
Expand Down Expand Up @@ -449,7 +457,7 @@ func (p *baseLogicalPlan) findBestTask(prop *property.PhysicalProperty, planCoun
goto END
}
opt.appendCandidate(p, curTask.plan(), prop)
if curIsBetter, err := compareTaskCost(p.ctx, curTask, bestTask); err != nil {
if curIsBetter, err := compareTaskCost(p.ctx, curTask, bestTask, opt); err != nil {
return nil, 0, err
} else if curIsBetter {
bestTask = curTask
Expand Down Expand Up @@ -881,7 +889,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter
}
appendCandidate(ds, idxMergeTask, prop, opt)

curIsBetter, err := compareTaskCost(ds.ctx, idxMergeTask, t)
curIsBetter, err := compareTaskCost(ds.ctx, idxMergeTask, t, opt)
if err != nil {
return nil, 0, err
}
Expand Down Expand Up @@ -973,7 +981,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter
cntPlan++
planCounter.Dec(1)
}
curIsBetter, cerr := compareTaskCost(ds.ctx, pointGetTask, t)
curIsBetter, cerr := compareTaskCost(ds.ctx, pointGetTask, t, opt)
if cerr != nil {
return nil, 0, cerr
}
Expand Down Expand Up @@ -1007,7 +1015,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter
planCounter.Dec(1)
}
appendCandidate(ds, tblTask, prop, opt)
curIsBetter, err := compareTaskCost(ds.ctx, tblTask, t)
curIsBetter, err := compareTaskCost(ds.ctx, tblTask, t, opt)
if err != nil {
return nil, 0, err
}
Expand All @@ -1032,7 +1040,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter
planCounter.Dec(1)
}
appendCandidate(ds, idxTask, prop, opt)
curIsBetter, err := compareTaskCost(ds.ctx, idxTask, t)
curIsBetter, err := compareTaskCost(ds.ctx, idxTask, t, opt)
if err != nil {
return nil, 0, err
}
Expand Down Expand Up @@ -1985,7 +1993,7 @@ func (ds *DataSource) convertToSampleTable(prop *property.PhysicalProperty,
}, nil
}

func (ds *DataSource) convertToPointGet(prop *property.PhysicalProperty, candidate *candidatePath, _ *physicalOptimizeOp) (task task) {
func (ds *DataSource) convertToPointGet(prop *property.PhysicalProperty, candidate *candidatePath, opt *physicalOptimizeOp) (task task) {
if !prop.IsSortItemEmpty() && !candidate.isMatchProp {
return invalidTask
}
Expand Down Expand Up @@ -2031,7 +2039,7 @@ func (ds *DataSource) convertToPointGet(prop *property.PhysicalProperty, candida
pointGetPlan.UnsignedHandle = mysql.HasUnsignedFlag(ds.handleCols.GetCol(0).RetType.GetFlag())
pointGetPlan.PartitionInfo = partitionInfo
pointGetPlan.accessCols = ds.TblCols
cost = pointGetPlan.GetCost()
cost = pointGetPlan.GetCost(opt)
// Add filter condition to table plan now.
if len(candidate.path.TableFilters) > 0 {
sessVars := ds.ctx.GetSessionVars()
Expand All @@ -2053,7 +2061,7 @@ func (ds *DataSource) convertToPointGet(prop *property.PhysicalProperty, candida
} else {
pointGetPlan.accessCols = ds.TblCols
}
cost = pointGetPlan.GetCost()
cost = pointGetPlan.GetCost(opt)
// Add index condition to table plan now.
if len(candidate.path.IndexFilters)+len(candidate.path.TableFilters) > 0 {
sessVars := ds.ctx.GetSessionVars()
Expand Down
5 changes: 4 additions & 1 deletion planner/core/optimizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,10 @@ func physicalOptimize(logic LogicalPlan, planCounter *PlanCounterTp) (plan Physi
opt := defaultPhysicalOptimizeOption()
stmtCtx := logic.SCtx().GetSessionVars().StmtCtx
if stmtCtx.EnableOptimizeTrace {
tracer := &tracing.PhysicalOptimizeTracer{Candidates: make(map[int]*tracing.CandidatePlanTrace)}
tracer := &tracing.PhysicalOptimizeTracer{
PhysicalPlanCostDetails: make(map[int]*tracing.PhysicalPlanCostDetail),
Candidates: make(map[int]*tracing.CandidatePlanTrace),
}
opt = opt.withEnableOptimizeTracer(tracer)
defer func() {
if err == nil {
Expand Down
31 changes: 30 additions & 1 deletion planner/core/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ type PhysicalPlan interface {
Plan

// GetPlanCost calculates the cost of the plan if it has not been calculated yet and returns the cost.
GetPlanCost(taskType property.TaskType, costFlag uint64) (float64, error)
GetPlanCost(taskType property.TaskType, option *PlanCostOption) (float64, error)

// attach2Task makes the current physical plan as the father of task's physicalPlan and updates the cost of
// current task. If the child's task is cop task, some operator may close this task and return a new rootTask.
Expand Down Expand Up @@ -381,6 +381,35 @@ type PhysicalPlan interface {
appendChildCandidate(op *physicalOptimizeOp)
}

// NewDefaultPlanCostOption returns PlanCostOption
func NewDefaultPlanCostOption() *PlanCostOption {
return &PlanCostOption{}
}

// PlanCostOption indicates option during GetPlanCost
type PlanCostOption struct {
CostFlag uint64
tracer *physicalOptimizeOp
}

// WithCostFlag set costflag
func (op *PlanCostOption) WithCostFlag(flag uint64) *PlanCostOption {
if op == nil {
return nil
}
op.CostFlag = flag
return op
}

// WithOptimizeTracer set tracer
func (op *PlanCostOption) WithOptimizeTracer(tracer *physicalOptimizeOp) *PlanCostOption {
if op == nil {
return nil
}
op.tracer = tracer
return op
}

type baseLogicalPlan struct {
basePlan

Expand Down
Loading

0 comments on commit 8e9e839

Please sign in to comment.