tidb rule_join_reorder_greedy 源码
tidb rule_join_reorder_greedy 代码
文件路径:/planner/core/rule_join_reorder_greedy.go
// Copyright 2018 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package core
import (
"math"
"sort"
"github.com/pingcap/tidb/expression"
)
type joinReorderGreedySolver struct {
*baseSingleGroupJoinOrderSolver
}
// solve reorders the join nodes in the group based on a greedy algorithm.
//
// For each node having a join equal condition with the current join tree in
// the group, calculate the cumulative join cost of that node and the join
// tree, choose the node with the smallest cumulative cost to join with the
// current join tree.
//
// cumulative join cost = CumCount(lhs) + CumCount(rhs) + RowCount(join)
//
// For base node, its CumCount equals to the sum of the count of its subtree.
// See baseNodeCumCost for more details.
//
// TODO: this formula can be changed to real physical cost in future.
//
// For the nodes and join trees which don't have a join equal condition to
// connect them, we make a bushy join tree to do the cartesian joins finally.
func (s *joinReorderGreedySolver) solve(joinNodePlans []LogicalPlan, tracer *joinReorderTrace) (LogicalPlan, error) {
var err error
s.curJoinGroup, err = s.generateJoinOrderNode(joinNodePlans, tracer)
if err != nil {
return nil, err
}
var leadingJoinNodes []*jrNode
if s.leadingJoinGroup != nil {
// We have a leading hint to let some tables join first. The result is stored in the s.leadingJoinGroup.
// We generate jrNode separately for it.
leadingJoinNodes, err = s.generateJoinOrderNode([]LogicalPlan{s.leadingJoinGroup}, tracer)
if err != nil {
return nil, err
}
}
// Sort plans by cost
sort.SliceStable(s.curJoinGroup, func(i, j int) bool {
return s.curJoinGroup[i].cumCost < s.curJoinGroup[j].cumCost
})
// joinNodeNum indicates the number of join nodes except leading join nodes in the current join group
joinNodeNum := len(s.curJoinGroup)
if leadingJoinNodes != nil {
// The leadingJoinNodes should be the first element in the s.curJoinGroup.
// So it can be joined first.
leadingJoinNodes := append(leadingJoinNodes, s.curJoinGroup...)
s.curJoinGroup = leadingJoinNodes
}
var cartesianGroup []LogicalPlan
for len(s.curJoinGroup) > 0 {
newNode, err := s.constructConnectedJoinTree(tracer)
if err != nil {
return nil, err
}
if joinNodeNum > 0 && len(s.curJoinGroup) == joinNodeNum {
// Getting here means that there is no join condition between the table used in the leading hint and other tables
// For example: select /*+ leading(t3) */ * from t1 join t2 on t1.a=t2.a cross join t3
// We can not let table t3 join first.
s.ctx.GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack("leading hint is inapplicable, check if the leading hint table has join conditions with other tables"))
}
cartesianGroup = append(cartesianGroup, newNode.p)
}
return s.makeBushyJoin(cartesianGroup), nil
}
func (s *joinReorderGreedySolver) constructConnectedJoinTree(tracer *joinReorderTrace) (*jrNode, error) {
curJoinTree := s.curJoinGroup[0]
s.curJoinGroup = s.curJoinGroup[1:]
for {
bestCost := math.MaxFloat64
bestIdx := -1
var finalRemainOthers []expression.Expression
var bestJoin LogicalPlan
for i, node := range s.curJoinGroup {
newJoin, remainOthers := s.checkConnectionAndMakeJoin(curJoinTree.p, node.p)
if newJoin == nil {
continue
}
_, err := newJoin.recursiveDeriveStats(nil)
if err != nil {
return nil, err
}
curCost := s.calcJoinCumCost(newJoin, curJoinTree, node)
tracer.appendLogicalJoinCost(newJoin, curCost)
if bestCost > curCost {
bestCost = curCost
bestJoin = newJoin
bestIdx = i
finalRemainOthers = remainOthers
}
}
// If we could find more join node, meaning that the sub connected graph have been totally explored.
if bestJoin == nil {
break
}
curJoinTree = &jrNode{
p: bestJoin,
cumCost: bestCost,
}
s.curJoinGroup = append(s.curJoinGroup[:bestIdx], s.curJoinGroup[bestIdx+1:]...)
s.otherConds = finalRemainOthers
}
return curJoinTree, nil
}
func (s *joinReorderGreedySolver) checkConnectionAndMakeJoin(leftPlan, rightPlan LogicalPlan) (LogicalPlan, []expression.Expression) {
leftPlan, rightPlan, usedEdges, joinType := s.checkConnection(leftPlan, rightPlan)
if len(usedEdges) == 0 {
return nil, nil
}
return s.makeJoin(leftPlan, rightPlan, usedEdges, joinType)
}
相关信息
相关文章
tidb collect_column_stats_usage 源码
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦