forked from cockroachdb/cockroach
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
opt: locality optimized scan for queries with a LIMIT clause
This commit adds locality optimized scan support for queries which place a hard limit on the number of rows returned via the LIMIT clause. This optimization benefits tables with REGIONAL BY ROW locality by splitting the spans accessed into a local spans set and a remote spans set, combined via a UNION ALL operation where each branch of the UNION ALL has the same hard limit as the original SELECT query block. If the limit is reached by scanning just the local spans, then latency is improved. The optimization is not applied if the LIMIT is more than the KV batch size of 100000 rows or if the number of spans in the scan exceeds 10000. This commit also adds an improvement to span merging to avoid merging local spans with remote spans in order to maximize the number of queries that can utilize locality optimized scan. Informs cockroachdb#64862 Release note (Performance Improvement): Queries with a LIMIT clause applied against a single table, either explicitly written, or implicit such as in an uncorrelated EXISTS subquery, now scan that table with improved latency if the table is defined with LOCALITY REGIONAL BY ROW and the number of qualified rows residing in the local region is less than or equal to the hard limit (sum of the LIMIT clause and optional OFFSET clause values). This optimization is only applied if the hard limit is 100000 or less.
- Loading branch information
Mark Sirek
committed
Feb 4, 2022
1 parent
1e96583
commit 68c25df
Showing
23 changed files
with
2,052 additions
and
414 deletions.
There are no files selected for viewing
592 changes: 592 additions & 0 deletions
592
pkg/ccl/logictestccl/testdata/logic_test/regional_by_row_query_behavior
Large diffs are not rendered by default.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
// Copyright 2022 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package constraint | ||
|
||
import ( | ||
"math" | ||
"sort" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/sql/opt/partition" | ||
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree" | ||
) | ||
|
||
// compare compares the key prefix in prefixInfo with the span prefix. The key | ||
// prefix is considered less than the span prefix if it is longer than the | ||
// span prefix, or if it sorts less according to the Datum.Compare interface. | ||
func compare(prefixInfo partition.PrefixIsLocal, span *Span, ps *partition.PrefixSorter) int { | ||
prefix := prefixInfo.Prefix | ||
prefixLength := len(prefix) | ||
spanPrefixLength := span.Prefix(ps.EvalCtx) | ||
// Longer prefixes sort before shorter ones. | ||
// The span prefix is allowed to be longer than the partition prefix and still | ||
// match. | ||
if prefixLength > spanPrefixLength { | ||
return -1 | ||
} | ||
|
||
// Look for an exact match on the shared prefix. | ||
for k, datum := range prefix { | ||
compareResult := datum.Compare(ps.EvalCtx, span.StartKey().Value(k)) | ||
if compareResult != 0 { | ||
return compareResult | ||
} | ||
} | ||
return 0 | ||
} | ||
|
||
// searchPrefixes searches a sorted slice of PrefixIsLocals in ps for a full | ||
// match on all Datums in the Prefix with the given span, and returns the index | ||
// of the match, or -1 if there is no match. | ||
// The slice must be sorted in ascending order, with longer prefixes sorting | ||
// before short prefixes and sorted by prefix values within each group | ||
// of equal-length prefixes. | ||
// Each equal-length prefix group is searched separately because there could be | ||
// more than one possible match for a given span, and we want to match the | ||
// longest-length prefix possible, because that reflects the actual locality | ||
// of the span's owning range. | ||
// If prefixSearchUpperBound is positive, only equal-length prefix groups of | ||
// length prefixSearchUpperBound or less will be searched. | ||
func searchPrefixes(span *Span, ps *partition.PrefixSorter, prefixSearchUpperBound int) int { | ||
if prefixSearchUpperBound <= 0 { | ||
prefixSearchUpperBound = math.MaxInt32 | ||
} | ||
spanPrefix := span.Prefix(ps.EvalCtx) | ||
i := 0 | ||
// Get the first slice in the PrefixSorter | ||
prefixSlice, startIndex, ok := ps.Slice(i) | ||
|
||
// return 'prefix >= span' result | ||
matchFunction := func(i int) bool { | ||
prefix := prefixSlice[i].Prefix | ||
// For nonzero-length partition prefixes, the span prefix must be at least | ||
// as long as it in order to match, whereas zero-length default partitions | ||
// match anything. | ||
if len(prefix) > spanPrefix { | ||
return false | ||
} else if len(prefix) == 0 { | ||
return true | ||
} | ||
|
||
for k, datum := range prefix { | ||
compareResult := datum.Compare(ps.EvalCtx, span.StartKey().Value(k)) | ||
if compareResult != 0 { | ||
return compareResult > 0 | ||
} | ||
} | ||
return true | ||
} | ||
|
||
for ; ok; prefixSlice, startIndex, ok = ps.Slice(i) { | ||
i++ | ||
|
||
if len(prefixSlice[0].Prefix) > prefixSearchUpperBound { | ||
continue | ||
} | ||
|
||
// Binary search for matching entry or insertion point in the prefix slices. | ||
index := sort.Search(len(prefixSlice), matchFunction) | ||
if index >= len(prefixSlice) { | ||
continue | ||
} | ||
// Need to requalify for equality because we might have just found an | ||
// insertion point instead of an actual match. | ||
if compare(prefixSlice[index], span, ps) == 0 { | ||
return index + startIndex | ||
} | ||
} | ||
return -1 | ||
} | ||
|
||
// FindMatch finds the Entry in PrefixSorter which matches the span prefix on a | ||
// prefix subset of its keys, including a zero-length match in the case of the | ||
// DEFAULT partition. | ||
func FindMatch(span *Span, ps *partition.PrefixSorter) (match *partition.PrefixIsLocal, ok bool) { | ||
index := searchPrefixes(span, ps, -1 /* prefixSearchUpperBound*/) | ||
if index == -1 { | ||
return nil, false | ||
} | ||
return &ps.Entry[index], true | ||
} | ||
|
||
// FindMatchOnSingleColumn finds the Entry in PrefixSorter with a prefix length | ||
// of 1 or less which matches the span prefix, including a zero-length match in | ||
// the case of the DEFAULT partition. | ||
func FindMatchOnSingleColumn( | ||
datum tree.Datum, ps *partition.PrefixSorter, | ||
) (match *partition.PrefixIsLocal, ok bool) { | ||
sp := &Span{} | ||
key := Key{firstVal: datum} | ||
sp.Init(key, IncludeBoundary, key, IncludeBoundary) | ||
index := searchPrefixes(sp, ps, 1 /* prefixSearchUpperBound */) | ||
if index == -1 { | ||
return nil, false | ||
} | ||
return &ps.Entry[index], true | ||
} |
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.