Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gen4 cost calculation tweak #8429

Merged
merged 3 commits into from
Jul 8, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion go/vt/vtgate/engine/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,14 @@ func (code RouteOpcode) MarshalJSON() ([]byte, error) {
return json.Marshal(routeName[code])
}

// String returns a string presentation of this opcode
func (code RouteOpcode) String() string {
return routeName[code]
}

// RouteType returns a description of the query routing type used by the primitive
func (route *Route) RouteType() string {
return routeName[route.Opcode]
return route.Opcode.String()
}

// GetKeyspaceName specifies the Keyspace that this primitive routes to.
Expand Down
81 changes: 68 additions & 13 deletions go/vt/vtgate/planbuilder/jointree.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ type (
pred sqlparser.Expr
}

// cost is used to make it easy to compare the cost of two plans with each other
cost struct {
vindexCost int
isUnique bool
opCode engine.RouteOpcode
}

routePlan struct {
routeOpCode engine.RouteOpcode
solved semantics.TableSet
Expand All @@ -76,7 +83,8 @@ type (
// leftJoins are the join conditions evaluated by this plan
leftJoins []*outerTable

// vindex and vindexValues is set if a vindex will be used for this route.
// these fields are set if a vindex will be used for this route
currentCost cost // currentCost tracks the cost of the chosen access method
vindex vindexes.Vindex
vindexValues []sqltypes.PlanValue
vindexPredicates []sqlparser.Expr
Expand All @@ -103,6 +111,17 @@ type (
}

parenTables []relation

// vindexPlusPredicates is a struct used to store all the predicates that the vindex can be used to query
vindexPlusPredicates struct {
colVindex *vindexes.ColumnVindex
values []sqltypes.PlanValue

// when we have the predicates found, we also know how to interact with this vindex
foundVindex vindexes.Vindex
opcode engine.RouteOpcode
predicates []sqlparser.Expr
}
)

// type assertions
Expand Down Expand Up @@ -217,17 +236,6 @@ func (rp *routePlan) cost() int {
return 1
}

// vindexPlusPredicates is a struct used to store all the predicates that the vindex can be used to query
type vindexPlusPredicates struct {
colVindex *vindexes.ColumnVindex
values []sqltypes.PlanValue

// when we have the predicates found, we also know how to interact with this vindex
foundVindex vindexes.Vindex
opcode engine.RouteOpcode
predicates []sqlparser.Expr
}

// addPredicate adds these predicates added to it. if the predicates can help,
// they will improve the routeOpCode
func (rp *routePlan) addPredicate(predicates ...sqlparser.Expr) error {
Expand Down Expand Up @@ -552,7 +560,9 @@ func (rp *routePlan) pickBestAvailableVindex() {
continue
}
// Choose the minimum cost vindex from the ones which are covered
if rp.vindex == nil || v.colVindex.Vindex.Cost() < rp.vindex.Cost() {
thisCost := costFor(v.foundVindex, v.opcode)
if rp.vindex == nil || less(thisCost, rp.currentCost) {
rp.currentCost = thisCost
rp.routeOpCode = v.opcode
rp.vindex = v.foundVindex
rp.vindexValues = v.values
Expand Down Expand Up @@ -633,3 +643,48 @@ func (jp *joinPlan) pushOutputColumns(columns []*sqlparser.ColName, semTable *se
}
return outputColumns
}

// costFor returns a cost struct to make route choices easier to compare
func costFor(foundVindex vindexes.Vindex, opcode engine.RouteOpcode) cost {
switch opcode {
// For these opcodes, we should not have a vindex, so we just return the opcode as the cost
case engine.SelectUnsharded, engine.SelectNext, engine.SelectDBA, engine.SelectReference, engine.SelectNone, engine.SelectScatter:
return cost{
opCode: opcode,
}
}

// if we have a multiplier that is non-zero, we should have a vindex
if foundVindex == nil {
panic("expected a vindex")
}

return cost{
vindexCost: foundVindex.Cost(),
isUnique: foundVindex.IsUnique(),
opCode: opcode,
}
}

// opCodePrecedence tracks the order that op codes are compared to each other
var opCodePrecedence = map[engine.RouteOpcode]int{
engine.SelectEqualUnique: 1,
engine.SelectEqual: 2,
engine.SelectIN: 3,
engine.SelectMultiEqual: 4,
engine.SelectScatter: 5,
}

// less compares two costs and returns true if the first cost is cheaper than the second
func less(c1, c2 cost) bool {
precedence1 := opCodePrecedence[c1.opCode]
precedence2 := opCodePrecedence[c2.opCode]
switch {
case precedence1 != precedence2:
return precedence1 < precedence2
case c1.isUnique == c2.isUnique:
return c1.vindexCost <= c2.vindexCost
default:
return c1.isUnique
}
}
systay marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/filter_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,7 @@ Gen4 plan same as above
"Vindex": "name_user_map"
}
}
Gen4 plan same as above

# Route with multiple route constraints, SelectEqual is the best constraint.
"select id from user where user.col = false and user.id in (1, 2) and user.name = 'aa'"
Expand All @@ -1007,6 +1008,7 @@ Gen4 plan same as above
"Vindex": "name_user_map"
}
}
Gen4 plan same as above

# Route with multiple route constraints, SelectEqualUnique is the best constraint.
"select id from user where user.col = 5 and user.id in (1, 2) and user.name = 'aa' and user.id = 1"
Expand Down