[YSQL] Support OR clauses as index predicates in LSM based IndexScan #18004
Labels
area/ysql
Yugabyte SQL (YSQL)
kind/enhancement
This is an enhancement of an existing feature
priority/low
Low priority
Jira Link: DB-7065
Description
Overview
IndexScan supports both discrete filters such as
key IN (a, b, c)
, range filters such askey >= a
, and a conjunction of such filters. However, LSM indexes currently do not support OR clauses.Even though, the predicate
key = a OR key = b OR key = c
is equivalent tokey IN (a, b, c)
, the former is not recognized as an index predicate, see #4634. More generally, many expressions can be rewritten as a disjunction of disjoint ranges each defined purely by conjunctive clauses. For example, the expression(key1 = a OR key1 = b) AND key2 > c
can be rewritten as(key1 = a AND key2 > c) OR (key1 = b AND key2 > c)
. Rows matchingkey1 = a AND key2 > c
are disjoint with rows matchingkey1 = b AND key2 > c
whena != b
and equivalent otherwise.Current Support
Index Clause support in Postgres
Postgres currently has poor support for index clauses that span multiple columns.
In the current architecture, each clause must correspond to a unique column. Moreover, selectivity computation may become more involved when index supports multicolumn clauses. See the comment below taken from the postgres code
Another caveat is when considering scalar array ops on a non-leading column
Will have to investigate why we skip these clauses in the index with non-leading columns. It might give some insight into similar scenarios with OR clauses as well. The following excerpt from the comment on RestrictInfo is a potential clue
Why care for multi-column clauses?
Because the storage layer supports them, see #14068.
Consider a simple query such as
Observe that the plan
is not indexed despite support from the storage layer. However, when we execute the same query on the latest version of Postgres, we get the following plan (also see #4634)
A bit surprising considering that DocDB supports these queries even better using HybridScan (colloquially known as skip scan).
More importantly, supporting disjunctions that act on more than column require support for multi-column clauses. Above query is one such example. Consider another simple example query
This issue is especially nasty when working with DISTINCT queries (same query but DISTINCT)
The disjoint ranges
r1 < 2
, andr1 >= 2 AND r2 > 2
each may have very few unique entries and an explicit Sequential Scan can be several orders of magnitude slower. In this case, even though postgres can generate a bitmap scan for the query, using a skip scan is so much better when there are only a few distinct queries. Because, well, we can skip over a lot of duplicate entries and thus avoid scanning them entirely.All this discussion on multi-column support raises a natural question on how postgres supports them in BitmapScan. Investigating this might lead us to a solution to this problem.
Why not rewrite the query to be a UNION ALL of disjoint ranges?
All the predicates discussed so far can be written as a disjoint union of ranges that can be handled well by the LSM index. However, this is not always the case. Consider a query such as
Only the first clause is indexable while the second clause is not. Converting this clause to conjunctive normal form is counter-productive since the clause
(r1 = 2 AND f(r2) = 0) OR (r1 = 2 AND f(r3) = 4)
. It is not clear how one would extract disjoint ranges satisfying the predicate.However, when the top level operation is an AND, postgres can pick any supported clause to be served by the index and then further filter the returned rows. In the above example, say the index can serve rows matching
r1 = 2
, then the planner can tack on another filterf(r2) = 0 OR f(r3) = 4
to further filter the rows. This post index scan filtering is not possible on the arms of an OR predicate instead.Query Layer LSM Index
DocDB's LSM-based index supports more operators than the B-Tree Index. Specifying these differences might require changes in
yb_lsm.c
. Need to adjust the cost model appropriately as well since index scans can be cheaper.Scan Order
Consider a query such as
produces a plan such as
which is inefficient since we can do a backward scan on r2 for each distinct element of r1. This is true if if the table resides in one colocation. If the table is partitioned a slightly more involved algorithm is necessary to combine the elements from different tablet servers.
Parallel Query
Will have to investigate how relevant #17984 is, especially since we might be able to retrieve disjoint ranges in parallel.
Efficiency Caveat
What's the number of disjoint ranges produced using this method?
In the worst case, all the OR conditions are in the last level of the expression tree. This can lead to an exponential growth in the number of clauses in DNF. Memory usage only has a polynomial advantage and hence is also exponential in the worst case. The predicate sizes are small enough that it should not matter in practice.
Example: Consider a predicate such as$p0 \wedge (p1 \vee p2 \vee ...) = (p0 \wedge p1) \vee ...$ When p0 itself constitutes an OR, then the number of disjunction simply doubles by having a single more AND clause.
ScanSpec
TODO: Better Explanation
TBD. Unclear what is supported. Requires testing. The current logic that computes the intersection/union of ranges is quite suspicious.
Queries to test
The range is determined as [2, 4]. A bit more non-trivial query we may want to consider
The above query must not return (1, 1).
The suspicion comes from the fact that the union and intersection is too simplistic and simply takes adjusts lower and upper bounds of each individual key column separately. TBD: Does the current implementation always return a superset of the required rows?
HybridScanChoices
HybridScanChoices supports predicates using an option list data structure. In combination with the notion of column groups, the module is able to support arbitrary DNF predicates as long as the atoms are supported index predicates (TBD: any exceptions?).
Key column in multiple groups
Example predicate
HybridScanChoices shouldn't get confused what column group a particular column belongs to.
Example predicates
Expected Gains
SELECT DISTINCT
After #17607, supporting more queries in ScanChoices implies that the distinct pushdown feature is more broadly applicable.
Related: #17741
Motivating Query
Consider the following query
This can be rewritten as
There is a significant drop in execution time from 1035ms to 4ms for equivalent queries. Would be nice to not have to rewrite queries such as these. Moreover, the planner can optimize away the top level de-duplication step once it realizes that the ranges are disjoint
r1 < 2
,r1 >= 2 AND r2 > 2
.Selective Queries
Currently, even point queries such as
execute a sequential scan which is expensive
The current workaround involves modifying the query to execute
which gives a much better plan
We can also execute
since there can be no duplicates across the two constituent SELECT queries.
Warning: Please confirm that this issue does not contain any sensitive information
The text was updated successfully, but these errors were encountered: