-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
planner: improve skyline pruning #26271
Merged
ti-chi-bot
merged 14 commits into
pingcap:master
from
xuyifangreeneyes:improve-skyline-pruning-2
Aug 2, 2021
Merged
Changes from 10 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
d9d8ce2
refine index back factor of skyline prunning
xuyifangreeneyes b45895a
fix test case
xuyifangreeneyes 25b39f5
enhance isMatchProp
xuyifangreeneyes 6ad7d5c
fix ut
xuyifangreeneyes d246484
add test for isMatchProp
xuyifangreeneyes 0685f15
fmt
xuyifangreeneyes 486fdc7
add comment
xuyifangreeneyes 7decc45
enhance detection of constant columns
xuyifangreeneyes b2d975c
fix ut & add comment
xuyifangreeneyes 116e60b
Merge branch 'master' into improve-skyline-pruning-2
xuyifangreeneyes aadd749
minor fix
xuyifangreeneyes a5ae5a4
minor fix
xuyifangreeneyes cf39a2c
minor fix
xuyifangreeneyes c33aa50
Merge branch 'master' into improve-skyline-pruning-2
ti-chi-bot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -414,10 +414,11 @@ func (ds *DataSource) tryToGetDualTask() (task, error) { | |
|
||
// candidatePath is used to maintain required info for skyline pruning. | ||
type candidatePath struct { | ||
path *util.AccessPath | ||
columnSet *intsets.Sparse // columnSet is the set of columns that occurred in the access conditions. | ||
isSingleScan bool | ||
isMatchProp bool | ||
path *util.AccessPath | ||
accessCondsColSet *intsets.Sparse // accessCondsColSet is the set of columns that occurred in the access conditions. | ||
indexFiltersColSet *intsets.Sparse // indexFiltersColSet is the set of columns that occurred in the index filters. | ||
isSingleScan bool | ||
isMatchProp bool | ||
} | ||
|
||
// compareColumnSet will compares the two set. The last return value is used to indicate | ||
|
@@ -450,18 +451,31 @@ func compareBool(l, r bool) int { | |
return 1 | ||
} | ||
|
||
func compareIndexBack(lhs, rhs *candidatePath) (int, bool) { | ||
result := compareBool(lhs.isSingleScan, rhs.isSingleScan) | ||
if result == 0 && !lhs.isSingleScan { | ||
// if both lhs and rhs need to access table after IndexScan, we use the set of columns that occurred in IndexFilters | ||
// to compare how many table rows will be accessed. | ||
return compareColumnSet(lhs.indexFiltersColSet, rhs.indexFiltersColSet) | ||
} | ||
return result, true | ||
} | ||
|
||
// compareCandidates is the core of skyline pruning. It compares the two candidate paths on three dimensions: | ||
// (1): the set of columns that occurred in the access condition, | ||
// (2): whether or not it matches the physical property | ||
// (3): does it require a double scan. | ||
// If `x` is not worse than `y` at all factors, | ||
// and there exists one factor that `x` is better than `y`, then `x` is better than `y`. | ||
func compareCandidates(lhs, rhs *candidatePath) int { | ||
setsResult, comparable := compareColumnSet(lhs.columnSet, rhs.columnSet) | ||
setsResult, comparable := compareColumnSet(lhs.accessCondsColSet, rhs.accessCondsColSet) | ||
if !comparable { | ||
return 0 | ||
} | ||
scanResult, comparable := compareIndexBack(lhs, rhs) | ||
if !comparable { | ||
return 0 | ||
} | ||
scanResult := compareBool(lhs.isSingleScan, rhs.isSingleScan) | ||
matchResult := compareBool(lhs.isMatchProp, rhs.isMatchProp) | ||
sum := setsResult + scanResult + matchResult | ||
if setsResult >= 0 && scanResult >= 0 && matchResult >= 0 && sum > 0 { | ||
|
@@ -473,52 +487,70 @@ func compareCandidates(lhs, rhs *candidatePath) int { | |
return 0 | ||
} | ||
|
||
func (ds *DataSource) getTableCandidate(path *util.AccessPath, prop *property.PhysicalProperty) *candidatePath { | ||
candidate := &candidatePath{path: path} | ||
func (ds *DataSource) isMatchProp(path *util.AccessPath, prop *property.PhysicalProperty) bool { | ||
var isMatchProp bool | ||
if path.IsIntHandlePath { | ||
pkCol := ds.getPKIsHandleCol() | ||
if len(prop.SortItems) == 1 && pkCol != nil { | ||
candidate.isMatchProp = prop.SortItems[0].Col.Equal(nil, pkCol) | ||
isMatchProp = prop.SortItems[0].Col.Equal(nil, pkCol) | ||
if path.StoreType == kv.TiFlash { | ||
candidate.isMatchProp = candidate.isMatchProp && !prop.SortItems[0].Desc | ||
isMatchProp = isMatchProp && !prop.SortItems[0].Desc | ||
} | ||
} | ||
} else { | ||
all, _ := prop.AllSameOrder() | ||
// When the prop is empty or `all` is false, `isMatchProp` is better to be `false` because | ||
// it needs not to keep order for index scan. | ||
if !prop.IsEmpty() && all { | ||
for i, col := range path.IdxCols { | ||
if col.Equal(nil, prop.SortItems[0].Col) { | ||
candidate.isMatchProp = matchIndicesProp(path.IdxCols[i:], path.IdxColLens[i:], prop.SortItems) | ||
return isMatchProp | ||
} | ||
all, _ := prop.AllSameOrder() | ||
// When the prop is empty or `all` is false, `isMatchProp` is better to be `false` because | ||
// it needs not to keep order for index scan. | ||
|
||
// Basically, if `prop.SortItems` is the prefix of `path.IdxCols`, then `isMatchProp` is true. However, we need to consider | ||
// the situations when some columns of `path.IdxCols` are evaluated as constant. For example: | ||
// ``` | ||
// create table t(a int, b int, c int, d int, index idx_a_b_c(a, b, c), index idx_d_c_b_a(d, c, b, a)); | ||
// select * from t where a = 1 order by b, c; | ||
// select * from t where b = 1 order by a, c; | ||
// select * from t where d = 1 and b = 2 order by c, a; | ||
// select * from t where d = 1 and b = 2 order by c, b, a; | ||
// ``` | ||
// In the first two `SELECT` statements, `idx_a_b_c` matches the sort order. In the last two `SELECT` statements, `idx_d_c_b_a` | ||
// matches the sort order. Hence, we use `path.ConstCols` to deal with the above situations. | ||
if !prop.IsEmpty() && all && len(path.IdxCols) >= len(prop.SortItems) { | ||
isMatchProp = true | ||
i := 0 | ||
for _, sortItem := range prop.SortItems { | ||
found := false | ||
for ; i < len(path.IdxCols); i++ { | ||
if path.IdxColLens[i] == types.UnspecifiedLength && sortItem.Col.Equal(nil, path.IdxCols[i]) { | ||
found = true | ||
i++ | ||
break | ||
} else if i >= path.EqCondCount { | ||
} | ||
if path.ConstCols == nil || !path.ConstCols[i] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about adding one more condition |
||
break | ||
} | ||
} | ||
if !found { | ||
isMatchProp = false | ||
break | ||
} | ||
} | ||
} | ||
candidate.columnSet = expression.ExtractColumnSet(path.AccessConds) | ||
return isMatchProp | ||
} | ||
|
||
func (ds *DataSource) getTableCandidate(path *util.AccessPath, prop *property.PhysicalProperty) *candidatePath { | ||
candidate := &candidatePath{path: path} | ||
candidate.isMatchProp = ds.isMatchProp(path, prop) | ||
candidate.accessCondsColSet = expression.ExtractColumnSet(path.AccessConds) | ||
candidate.isSingleScan = true | ||
return candidate | ||
} | ||
|
||
func (ds *DataSource) getIndexCandidate(path *util.AccessPath, prop *property.PhysicalProperty, isSingleScan bool) *candidatePath { | ||
candidate := &candidatePath{path: path} | ||
all, _ := prop.AllSameOrder() | ||
// When the prop is empty or `all` is false, `isMatchProp` is better to be `false` because | ||
// it needs not to keep order for index scan. | ||
if !prop.IsEmpty() && all { | ||
for i, col := range path.IdxCols { | ||
if col.Equal(nil, prop.SortItems[0].Col) { | ||
candidate.isMatchProp = matchIndicesProp(path.IdxCols[i:], path.IdxColLens[i:], prop.SortItems) | ||
break | ||
} else if i >= path.EqCondCount { | ||
break | ||
} | ||
} | ||
} | ||
candidate.columnSet = expression.ExtractColumnSet(path.AccessConds) | ||
candidate.isMatchProp = ds.isMatchProp(path, prop) | ||
candidate.accessCondsColSet = expression.ExtractColumnSet(path.AccessConds) | ||
candidate.indexFiltersColSet = expression.ExtractColumnSet(path.IndexFilters) | ||
candidate.isSingleScan = isSingleScan | ||
return candidate | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any test case that can cover this rule?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. https://github.com/xuyifangreeneyes/tidb/blob/116e60b8ad38f7ebfa5ea68b09e87054723a9394/planner/core/logical_plan_test.go#L1703-L1710 In the two cases
f_g
is better thanf
due to this rule.