Skip to content

Commit

Permalink
optimizer/plan: address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
coocood committed Jan 19, 2016
1 parent 070ef99 commit 3e14b7e
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 47 deletions.
4 changes: 2 additions & 2 deletions optimizer/plan/cost.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (c *costEstimator) Leave(p Plan) (Plan, bool) {
func (c *costEstimator) tableScan(v *TableScan) {
var rowCount float64 = FullRangeCount
for _, con := range v.AccessConditions {
rowCount *= computeFilterRate(con)
rowCount *= guesstimateFilterRate(con)
}
v.startupCost = 0
if v.limit == 0 {
Expand All @@ -89,7 +89,7 @@ func (c *costEstimator) tableScan(v *TableScan) {
func (c *costEstimator) indexScan(v *IndexScan) {
var rowCount float64 = FullRangeCount
for _, con := range v.AccessConditions {
rowCount *= computeFilterRate(con)
rowCount *= guesstimateFilterRate(con)
}
v.startupCost = 0
if v.limit == 0 {
Expand Down
58 changes: 30 additions & 28 deletions optimizer/plan/filterrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,69 +19,71 @@ import (
)

const (
rateFull = 1
rateEqual = 0.001
rateNotEqual = 0.999
rateBetween = 0.3
rateGreaterOrLess = 0.4
rateIsFalse = 0.01
rateIsNull = 0.01
rateLike = 0.1
rateFull float64 = 1
rateEqual float64 = 0.01
rateNotEqual float64 = 0.99
rateBetween float64 = 0.1
rateGreaterOrLess float64 = 0.33
rateIsFalse float64 = 0.1
rateIsNull float64 = 0.1
rateLike float64 = 0.1
)

// computeFilterRate computes the filter rate for an expression.
// guesstimateFilterRate guesstimates the filter rate for an expression.
// For example: a table has 100 rows, after filter expression 'a between 0 and 9',
// 10 rows returned, then the filter rate is '0.1'.
// It only depends on the expression type, not the expression value.
// The expr parameter should contain only one column name.
func computeFilterRate(expr ast.ExprNode) float64 {
func guesstimateFilterRate(expr ast.ExprNode) float64 {
switch x := expr.(type) {
case *ast.BetweenExpr:
return rateBetween
case *ast.BinaryOperationExpr:
return computeBinopFilterRate(x)
return guesstimateBinop(x)
case *ast.ColumnNameExpr:
return rateFull
case *ast.IsNullExpr:
return computeIsNullFilterRate(x)
return guesstimateIsNull(x)
case *ast.IsTruthExpr:
return computeIsTrueFilterRate(x)
return guesstimateIsTrue(x)
case *ast.ParenthesesExpr:
return computeFilterRate(x.Expr)
return guesstimateFilterRate(x.Expr)
case *ast.PatternInExpr:
return computePatternInFilterRate(x)
return guesstimatePatternIn(x)
case *ast.PatternLikeExpr:
return computePatternLikeFilterRate(x)
return guesstimatePatternLike(x)
}
return rateFull
}

func computeBinopFilterRate(expr *ast.BinaryOperationExpr) float64 {
func guesstimateBinop(expr *ast.BinaryOperationExpr) float64 {
switch expr.Op {
case opcode.AndAnd:
return computeFilterRate(expr.L) * computeFilterRate(expr.R)
// P(A and B) = P(A) * P(B)
return guesstimateFilterRate(expr.L) * guesstimateFilterRate(expr.R)
case opcode.OrOr:
rate := computeFilterRate(expr.L) + computeFilterRate(expr.R)
if rate > rateFull {
rate = rateFull
}
return rate
// P(A or B) = P(A) + P(B) – P(A and B)
rateL := guesstimateFilterRate(expr.L)
rateR := guesstimateFilterRate(expr.R)
return rateL + rateR - rateL*rateR
case opcode.EQ:
return rateEqual
case opcode.GT, opcode.GE, opcode.LT, opcode.LE:
return rateGreaterOrLess
case opcode.NE:
return rateNotEqual
}
return 1
return rateFull
}

func computeIsNullFilterRate(expr *ast.IsNullExpr) float64 {
func guesstimateIsNull(expr *ast.IsNullExpr) float64 {
if expr.Not {
return rateFull - rateIsNull
}
return rateIsNull
}

func computeIsTrueFilterRate(expr *ast.IsTruthExpr) float64 {
func guesstimateIsTrue(expr *ast.IsTruthExpr) float64 {
if expr.True == 0 {
if expr.Not {
return rateFull - rateIsFalse
Expand All @@ -94,7 +96,7 @@ func computeIsTrueFilterRate(expr *ast.IsTruthExpr) float64 {
return rateFull - rateIsFalse - rateIsNull
}

func computePatternInFilterRate(expr *ast.PatternInExpr) float64 {
func guesstimatePatternIn(expr *ast.PatternInExpr) float64 {
if len(expr.List) > 0 {
rate := rateEqual * float64(len(expr.List))
if expr.Not {
Expand All @@ -105,7 +107,7 @@ func computePatternInFilterRate(expr *ast.PatternInExpr) float64 {
return rateFull
}

func computePatternLikeFilterRate(expr *ast.PatternLikeExpr) float64 {
func guesstimatePatternLike(expr *ast.PatternLikeExpr) float64 {
if expr.Not {
return rateFull - rateLike
}
Expand Down
4 changes: 2 additions & 2 deletions optimizer/plan/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,15 +220,15 @@ func (s *testPlanSuite) TestFilterRate(c *C) {
{expr: "a in (1, 2, 3)", rate: rateEqual * 3},
{expr: "a not in (1, 2, 3)", rate: rateFull - rateEqual*3},
{expr: "a > 1 and a < 9", rate: float64(rateGreaterOrLess) * float64(rateGreaterOrLess)},
{expr: "a = 1 or a = 2", rate: rateEqual + rateEqual},
{expr: "a = 1 or a = 2", rate: rateEqual + rateEqual - rateEqual*rateEqual},
{expr: "a != 1", rate: rateNotEqual},
}
for _, ca := range cases {
sql := "select 1 from dual where " + ca.expr
s, err := parser.ParseOneStmt(sql, "", "")
c.Assert(err, IsNil, Commentf("for expr %s", ca.expr))
stmt := s.(*ast.SelectStmt)
rate := computeFilterRate(stmt.Where)
rate := guesstimateFilterRate(stmt.Where)
c.Assert(rate, Equals, ca.rate, Commentf("for expr %s", ca.expr))
}
}
Expand Down
39 changes: 24 additions & 15 deletions optimizer/plan/planbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,16 @@ func (b *planBuilder) buildJoin(sel *ast.SelectStmt) Plan {

func (b *planBuilder) buildAllAccessMethodsPlan(tn *ast.TableName, conditions []ast.ExprNode) []Plan {
var candidates []Plan
p := b.buildTableScanPlan(tn, conditions)
candidates = append(candidates, p)
for _, index := range tn.TableInfo.Indices {
ip := b.buildIndexScanPlan(index, tn, conditions)
candidates = append(candidates, ip)
}
return candidates
}

func (b *planBuilder) buildTableScanPlan(tn *ast.TableName, conditions []ast.ExprNode) Plan {
p := &TableScan{
Table: tn.TableInfo,
}
Expand All @@ -236,24 +246,23 @@ func (b *planBuilder) buildAllAccessMethodsPlan(tn *ast.TableName, conditions []
p.FilterConditions = append(p.FilterConditions, con)
}
}
candidates = append(candidates, p)
return p
}

for _, index := range tn.TableInfo.Indices {
ip := &IndexScan{Table: tn.TableInfo, Index: index}
ip.SetFields(tn.GetResultFields())
// Only use first column as access condition for cost estimation,
// In executor, we can try to use second index column to build index range.
checker := conditionChecker{tableName: tn.TableInfo.Name, idx: index, columnOffset: 0}
for _, con := range conditions {
if checker.check(con) {
ip.AccessConditions = append(ip.AccessConditions, con)
} else {
ip.FilterConditions = append(ip.FilterConditions, con)
}
func (b *planBuilder) buildIndexScanPlan(index *model.IndexInfo, tn *ast.TableName, conditions []ast.ExprNode) Plan {
ip := &IndexScan{Table: tn.TableInfo, Index: index}
ip.SetFields(tn.GetResultFields())
// Only use first column as access condition for cost estimation,
// In executor, we can try to use second index column to build index range.
checker := conditionChecker{tableName: tn.TableInfo.Name, idx: index, columnOffset: 0}
for _, con := range conditions {
if checker.check(con) {
ip.AccessConditions = append(ip.AccessConditions, con)
} else {
ip.FilterConditions = append(ip.FilterConditions, con)
}
candidates = append(candidates, ip)
}
return candidates
return ip
}

// buildPseudoSelectPlan pre-builds more complete plans that may affect total cost.
Expand Down

0 comments on commit 3e14b7e

Please sign in to comment.