From e4f5f51a478ca2616ad6540a84a1e2bb50bd2a45 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Sat, 18 Apr 2020 02:04:44 +0800 Subject: [PATCH 1/3] reference/sql: add description about when partition pruning works --- reference/sql/partitioning.md | 110 ++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/reference/sql/partitioning.md b/reference/sql/partitioning.md index bd78f0cd6e82..340dd4919867 100644 --- a/reference/sql/partitioning.md +++ b/reference/sql/partitioning.md @@ -500,6 +500,116 @@ SELECT fname, lname, region_code, dob * partition_column = constant * partition_column IN (constant1, constant2, ..., constantN) +### 分区裁剪生效的场景 + +1. 分区裁剪需要使用分区表上面的查询条件,所以根据优化器的优化规则,如果查询条件不能下推到分区表,则相应的查询语句无法执行分区裁剪。 + +例如: + +{{< copyable "sql" >}} + +```sql +create table t1 (x int) partition by range (x) ( + partition p0 values less than (5), + partition p1 values less than (10)); +create table t2 (x int); +``` + +{{< copyable "sql" >}} + +```sql +explain select * from t1 left join t2 on t1.x = t2.x where t2.x > 5; +``` +在这个查询中,外连接可以简化成内连接,然后由 `t1.x = t2.x` 和 `t2.x > 5` 可以推出条件 `t1.x > 5`,于是可以分区裁剪只使用 `p1` 分区。 + +{{< copyable "sql" >}} + +```sql +explain select * from t1 left join t2 on t1.x = t2.x and t2.x > 5; +``` + +这个查询中的 `t2.x > 5` 的条件是不可以推到 `t1` 上面的,因此 `t1` 无法分区裁剪。 + +2. 由于分区裁剪的优化规则是在生成查询计划阶段,对于执行阶段才能获取到过滤条件的场景无法利用分区裁剪的优化 + +例如: + +{{< copyable "sql" >}} + +```sql +create table t1 (x int) partition by range (x) ( + partition p0 values less than (5), + partition p1 values less than (10)); +``` + +{{< copyable "sql" >}} + +```sql +explain select * from t2 where x < (select * from t1 where t2.x < t1.x and t2.x < 2); +``` + +这个查询每从 `t2` 读取一行,都会去分区表 `t1` 上进行查询,理论上这时会满足了 `t1.x > val` 的过滤条件,但实际由于分区裁剪只作用查询计划生成阶段,而不是执行阶段,因而不会做裁剪。 + + +3. 由于当前实现中的一处限制,导致对于查询条件无法下推到 TiKV 表达式,不支持分区裁剪 + +对于一个函数表达式 `fn(col)`,如果 TiKV 支持这个函数 `fn`,则在查询优化做谓词下推的时候,`fn(col)` 会被推到叶子节点(也就是分区),因而能够执行分区裁剪。 + +如果 TiKV 不支持 `fn`,则优化阶段不会把 `fn(col)` 推到叶子节点,而是在叶子上面连接一个 Selection 节点,分区裁剪的实现没有处理这种父亲节点的 Selection 中的条件,因此不能下推到 TiKV 的表达式不支持分区裁剪。 + + +4. 对于 hash 分区类型,只有等值比较的查询条件能够支持分区裁剪 + + +5. 对于 range 分区类型,分区表达式必须是 `col` 或者 `fn(col)` 的简单形式,查询条件是 > < = >= <= 时才能支持分区裁剪。如果分区表达式是 `fn(col)` 形式,还要求 `fn` 必须是单调函数,才有可能分区裁剪。 + +这里单调函数是指某个函数 `fn` 满足条件:对于任意 `x` `y`,如果 `x > y`,则 `fn(x) > fn(y)`。 + +这种是严格递增的单调函数,非严格递增的单调函数也可以符合分区裁剪要求,只要函数 `fn` 满足:对于任意 `x` `y`,如果 `x > y`,则 `fn(x) >= fn(y)`。 + +理论上所有满足单调条件(严格或者非严格)的函数都是可以支持分区裁剪。实际上,目前 TiDB 已经支持的单调函数只有: + +``` +unix_timestamp +to_days +``` + +例如,分区表达式是简单列的情况: + + +{{< copyable "sql" >}} + +```sql +create table t (id int) partition by range (id) ( + partition p0 values less than (5), + partition p1 values less than (10)); +select * from t where t > 6; +``` + +分区表达式是 `fn(col)` 的形式,`fn` 是我们支持的单调函数 `to_days`: + + +{{< copyable "sql" >}} + +```sql +create table t (dt datetime) partition by range (to_days(id)) ( + partition p0 values less than (to_days('2020-04-01')), + partition p1 values less than (to_days('2020-05-01'))); +select * from t where t > '2020-04-18'; +``` + +有一处例外是 `floor(unix_timestamp(ts))` 作为分区表达式,TiDB 针对这个场景做了特殊处理,可以支持分区裁剪。 + +{{< copyable "sql" >}} + +```sql +create table t (ts timestamp(3) not null default current_timestamp(3)) +partition by range (floor(unix_timestamp(ts))) ( + partition p0 values less than (unix_timestamp('2020-04-01 00:00:00')), + partition p1 values less than (unix_timestamp('2020-05-01 00:00:00'))); +select * from t where t > '2020-04-18 02:00:42.123'; +``` + ## 分区选择 SELECT 语句中支持分区选择。实现通过使用一个 `PARTITION` 选项实现。 From a293e89746a6b04d6ee599aa784a3dc727fa77b3 Mon Sep 17 00:00:00 2001 From: lilin90 Date: Mon, 20 Apr 2020 12:13:37 +0800 Subject: [PATCH 2/3] reference/sql: fix format --- reference/sql/partitioning.md | 138 +++++++++++++++++----------------- 1 file changed, 67 insertions(+), 71 deletions(-) diff --git a/reference/sql/partitioning.md b/reference/sql/partitioning.md index 340dd4919867..e47219c06486 100644 --- a/reference/sql/partitioning.md +++ b/reference/sql/partitioning.md @@ -504,111 +504,107 @@ SELECT fname, lname, region_code, dob 1. 分区裁剪需要使用分区表上面的查询条件,所以根据优化器的优化规则,如果查询条件不能下推到分区表,则相应的查询语句无法执行分区裁剪。 -例如: + 例如: -{{< copyable "sql" >}} + {{< copyable "sql" >}} -```sql -create table t1 (x int) partition by range (x) ( - partition p0 values less than (5), - partition p1 values less than (10)); -create table t2 (x int); -``` + ```sql + create table t1 (x int) partition by range (x) ( + partition p0 values less than (5), + partition p1 values less than (10)); + create table t2 (x int); + ``` -{{< copyable "sql" >}} + {{< copyable "sql" >}} -```sql -explain select * from t1 left join t2 on t1.x = t2.x where t2.x > 5; -``` -在这个查询中,外连接可以简化成内连接,然后由 `t1.x = t2.x` 和 `t2.x > 5` 可以推出条件 `t1.x > 5`,于是可以分区裁剪只使用 `p1` 分区。 + ```sql + explain select * from t1 left join t2 on t1.x = t2.x where t2.x > 5; + ``` -{{< copyable "sql" >}} + 在这个查询中,外连接可以简化成内连接,然后由 `t1.x = t2.x` 和 `t2.x > 5` 可以推出条件 `t1.x > 5`,于是可以分区裁剪只使用 `p1` 分区。 -```sql -explain select * from t1 left join t2 on t1.x = t2.x and t2.x > 5; -``` - -这个查询中的 `t2.x > 5` 的条件是不可以推到 `t1` 上面的,因此 `t1` 无法分区裁剪。 + {{< copyable "sql" >}} -2. 由于分区裁剪的优化规则是在生成查询计划阶段,对于执行阶段才能获取到过滤条件的场景无法利用分区裁剪的优化 + ```sql + explain select * from t1 left join t2 on t1.x = t2.x and t2.x > 5; + ``` -例如: + 这个查询中的 `t2.x > 5` 的条件是不可以推到 `t1` 上面的,因此 `t1` 无法分区裁剪。 -{{< copyable "sql" >}} +2. 由于分区裁剪的优化规则是在生成查询计划阶段,对于执行阶段才能获取到过滤条件的场景,无法利用分区裁剪的优化。 -```sql -create table t1 (x int) partition by range (x) ( - partition p0 values less than (5), - partition p1 values less than (10)); -``` - -{{< copyable "sql" >}} + 例如: -```sql -explain select * from t2 where x < (select * from t1 where t2.x < t1.x and t2.x < 2); -``` + {{< copyable "sql" >}} -这个查询每从 `t2` 读取一行,都会去分区表 `t1` 上进行查询,理论上这时会满足了 `t1.x > val` 的过滤条件,但实际由于分区裁剪只作用查询计划生成阶段,而不是执行阶段,因而不会做裁剪。 + ```sql + create table t1 (x int) partition by range (x) ( + partition p0 values less than (5), + partition p1 values less than (10)); + ``` + {{< copyable "sql" >}} -3. 由于当前实现中的一处限制,导致对于查询条件无法下推到 TiKV 表达式,不支持分区裁剪 + ```sql + explain select * from t2 where x < (select * from t1 where t2.x < t1.x and t2.x < 2); + ``` -对于一个函数表达式 `fn(col)`,如果 TiKV 支持这个函数 `fn`,则在查询优化做谓词下推的时候,`fn(col)` 会被推到叶子节点(也就是分区),因而能够执行分区裁剪。 + 这个查询每从 `t2` 读取一行,都会去分区表 `t1` 上进行查询,理论上这时会满足了 `t1.x > val` 的过滤条件,但实际由于分区裁剪只作用查询计划生成阶段,而不是执行阶段,因而不会做裁剪。 -如果 TiKV 不支持 `fn`,则优化阶段不会把 `fn(col)` 推到叶子节点,而是在叶子上面连接一个 Selection 节点,分区裁剪的实现没有处理这种父亲节点的 Selection 中的条件,因此不能下推到 TiKV 的表达式不支持分区裁剪。 +3. 由于当前实现中的一处限制,导致对于查询条件无法下推到 TiKV 表达式,不支持分区裁剪。 + 对于一个函数表达式 `fn(col)`,如果 TiKV 支持这个函数 `fn`,则在查询优化做谓词下推的时候,`fn(col)` 会被推到叶子节点(也就是分区),因而能够执行分区裁剪。 -4. 对于 hash 分区类型,只有等值比较的查询条件能够支持分区裁剪 + 如果 TiKV 不支持 `fn`,则优化阶段不会把 `fn(col)` 推到叶子节点,而是在叶子上面连接一个 Selection 节点,分区裁剪的实现没有处理这种父亲节点的 Selection 中的条件,因此不能下推到 TiKV 的表达式不支持分区裁剪。 +4. 对于 hash 分区类型,只有等值比较的查询条件能够支持分区裁剪。 5. 对于 range 分区类型,分区表达式必须是 `col` 或者 `fn(col)` 的简单形式,查询条件是 > < = >= <= 时才能支持分区裁剪。如果分区表达式是 `fn(col)` 形式,还要求 `fn` 必须是单调函数,才有可能分区裁剪。 -这里单调函数是指某个函数 `fn` 满足条件:对于任意 `x` `y`,如果 `x > y`,则 `fn(x) > fn(y)`。 + 这里单调函数是指某个函数 `fn` 满足条件:对于任意 `x` `y`,如果 `x > y`,则 `fn(x) > fn(y)`。 -这种是严格递增的单调函数,非严格递增的单调函数也可以符合分区裁剪要求,只要函数 `fn` 满足:对于任意 `x` `y`,如果 `x > y`,则 `fn(x) >= fn(y)`。 + 这种是严格递增的单调函数,非严格递增的单调函数也可以符合分区裁剪要求,只要函数 `fn` 满足:对于任意 `x` `y`,如果 `x > y`,则 `fn(x) >= fn(y)`。 -理论上所有满足单调条件(严格或者非严格)的函数都是可以支持分区裁剪。实际上,目前 TiDB 已经支持的单调函数只有: + 理论上所有满足单调条件(严格或者非严格)的函数都是可以支持分区裁剪。实际上,目前 TiDB 已经支持的单调函数只有: -``` -unix_timestamp -to_days -``` + ``` + unix_timestamp + to_days + ``` -例如,分区表达式是简单列的情况: + 例如,分区表达式是简单列的情况: + {{< copyable "sql" >}} -{{< copyable "sql" >}} + ```sql + create table t (id int) partition by range (id) ( + partition p0 values less than (5), + partition p1 values less than (10)); + select * from t where t > 6; + ``` -```sql -create table t (id int) partition by range (id) ( - partition p0 values less than (5), - partition p1 values less than (10)); -select * from t where t > 6; -``` + 分区表达式是 `fn(col)` 的形式,`fn` 是我们支持的单调函数 `to_days`: -分区表达式是 `fn(col)` 的形式,`fn` 是我们支持的单调函数 `to_days`: + {{< copyable "sql" >}} + ```sql + create table t (dt datetime) partition by range (to_days(id)) ( + partition p0 values less than (to_days('2020-04-01')), + partition p1 values less than (to_days('2020-05-01'))); + select * from t where t > '2020-04-18'; + ``` -{{< copyable "sql" >}} + 有一处例外是 `floor(unix_timestamp(ts))` 作为分区表达式,TiDB 针对这个场景做了特殊处理,可以支持分区裁剪。 -```sql -create table t (dt datetime) partition by range (to_days(id)) ( - partition p0 values less than (to_days('2020-04-01')), - partition p1 values less than (to_days('2020-05-01'))); -select * from t where t > '2020-04-18'; -``` + {{< copyable "sql" >}} -有一处例外是 `floor(unix_timestamp(ts))` 作为分区表达式,TiDB 针对这个场景做了特殊处理,可以支持分区裁剪。 - -{{< copyable "sql" >}} - -```sql -create table t (ts timestamp(3) not null default current_timestamp(3)) -partition by range (floor(unix_timestamp(ts))) ( - partition p0 values less than (unix_timestamp('2020-04-01 00:00:00')), - partition p1 values less than (unix_timestamp('2020-05-01 00:00:00'))); -select * from t where t > '2020-04-18 02:00:42.123'; -``` + ```sql + create table t (ts timestamp(3) not null default current_timestamp(3)) + partition by range (floor(unix_timestamp(ts))) ( + partition p0 values less than (unix_timestamp('2020-04-01 00:00:00')), + partition p1 values less than (unix_timestamp('2020-05-01 00:00:00'))); + select * from t where t > '2020-04-18 02:00:42.123'; + ``` ## 分区选择 From 9eaa3172d52e42c13df533408990dddaa92a0ba6 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Mon, 20 Apr 2020 13:42:53 +0800 Subject: [PATCH 3/3] Apply suggestions from code review Co-Authored-By: Lilian Lee --- reference/sql/partitioning.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/reference/sql/partitioning.md b/reference/sql/partitioning.md index e47219c06486..4b456c98406a 100644 --- a/reference/sql/partitioning.md +++ b/reference/sql/partitioning.md @@ -521,7 +521,7 @@ SELECT fname, lname, region_code, dob explain select * from t1 left join t2 on t1.x = t2.x where t2.x > 5; ``` - 在这个查询中,外连接可以简化成内连接,然后由 `t1.x = t2.x` 和 `t2.x > 5` 可以推出条件 `t1.x > 5`,于是可以分区裁剪只使用 `p1` 分区。 + 在这个查询中,外连接可以简化成内连接,然后由 `t1.x = t2.x` 和 `t2.x > 5` 可以推出条件 `t1.x > 5`,于是可以分区裁剪并且只使用 `p1` 分区。 {{< copyable "sql" >}} @@ -529,9 +529,9 @@ SELECT fname, lname, region_code, dob explain select * from t1 left join t2 on t1.x = t2.x and t2.x > 5; ``` - 这个查询中的 `t2.x > 5` 的条件是不可以推到 `t1` 上面的,因此 `t1` 无法分区裁剪。 + 这个查询中的 `t2.x > 5` 条件不能下推到 `t1` 分区表上面,因此 `t1` 无法分区裁剪。 -2. 由于分区裁剪的优化规则是在生成查询计划阶段,对于执行阶段才能获取到过滤条件的场景,无法利用分区裁剪的优化。 +2. 由于分区裁剪的规则优化是在查询计划的生成阶段,对于执行阶段才能获取到过滤条件的场景,无法利用分区裁剪的优化。 例如: @@ -549,13 +549,13 @@ SELECT fname, lname, region_code, dob explain select * from t2 where x < (select * from t1 where t2.x < t1.x and t2.x < 2); ``` - 这个查询每从 `t2` 读取一行,都会去分区表 `t1` 上进行查询,理论上这时会满足了 `t1.x > val` 的过滤条件,但实际由于分区裁剪只作用查询计划生成阶段,而不是执行阶段,因而不会做裁剪。 + 这个查询每从 `t2` 读取一行,都会去分区表 `t1` 上进行查询,理论上这时会满足 `t1.x > val` 的过滤条件,但实际上由于分区裁剪只作用于查询计划生成阶段,而不是执行阶段,因而不会做裁剪。 -3. 由于当前实现中的一处限制,导致对于查询条件无法下推到 TiKV 表达式,不支持分区裁剪。 +3. 由于当前实现中的一处限制,对于查询条件无法下推到 TiKV 的表达式,不支持分区裁剪。 对于一个函数表达式 `fn(col)`,如果 TiKV 支持这个函数 `fn`,则在查询优化做谓词下推的时候,`fn(col)` 会被推到叶子节点(也就是分区),因而能够执行分区裁剪。 - 如果 TiKV 不支持 `fn`,则优化阶段不会把 `fn(col)` 推到叶子节点,而是在叶子上面连接一个 Selection 节点,分区裁剪的实现没有处理这种父亲节点的 Selection 中的条件,因此不能下推到 TiKV 的表达式不支持分区裁剪。 + 如果 TiKV 不支持 `fn`,则优化阶段不会把 `fn(col)` 推到叶子节点,而是在叶子上面连接一个 Selection 节点,分区裁剪的实现没有处理这种父节点的 Selection 中的条件,因此对不能下推到 TiKV 的表达式不支持分区裁剪。 4. 对于 hash 分区类型,只有等值比较的查询条件能够支持分区裁剪。