分区裁剪本身是一个比较复杂的过程,优化器需要根据用户表的分区信息和 SQL 中给定的条件,抽取出相关的分区信息。由于 SQL 中的条件往往比较复杂,整个抽取逻辑的复杂性也随之增加,这一过程由 OceanBase 数据库中的 Query Range 子模块完成。

    以下示例中当用户使用如下 SQL 访问分区表时,由于 c1 为 1 的数据全部处于第 1 号分区(p1),实际上我们只需要访问该分区即可(避免访问第 0、2、3、4 号分区):

    通过 查看执行计划可以看到分区裁剪的结果:

    1. obclient> EXPLAIN SELECT * FROM t1 WHERE c1 = 1 \G
    2. *************************** 1. row ***************************
    3. Query Plan: ===================================
    4. |ID|OPERATOR |NAME|EST. ROWS|COST|
    5. -----------------------------------
    6. |0 |TABLE SCAN|t1 |1 |1303|
    7. ===================================
    8. Outputs & filters:
    9. -------------------------------------
    10. 0 - output([t1.c1], [t1.c2]), filter([t1.c1 = 1]),
    11. access([t1.c1], [t1.c2]), partitions(p1)

    分区裁剪就是根据 where 子句里面的条件并且计算得到分区列的值,然后通过结果判断需要访问哪些分区。如果分区函数为表达式,且该表达式作为一个整体出现在等值条件里,也可以做分区裁剪。

    示例如下:

    Range 分区

    下述示例中,分区条件为表达式,而查询条件为非等值条件(c1 < 150 and c1 > 100),则无法进行分区裁剪:

    1. obclient> CREATE TABLE t1
    2. (
    3. c1 INT,
    4. c2 INT
    5. )
    6. PARTITION BY RANGE(c1 + 1)
    7. (
    8. PARTITION p0 VALUES less than (100),
    9. PARTITION p1 VALUES less than (200)
    10. );
    11. obclient> EXPLAIN SELECT * FROM t1 WHERE c1 < 150 and c1 > 110 \G
    12. *************************** 1. row ***************************
    13. Query Plan: ============================================
    14. |ID|OPERATOR |NAME|EST. ROWS|COST|
    15. --------------------------------------------
    16. |0 |EXCHANGE IN DISTR | |19 |1410|
    17. |1 | EXCHANGE OUT DISTR| |19 |1303|
    18. |2 | TABLE SCAN |t1 |19 |1303|
    19. ============================================
    20. Outputs & filters:
    21. -------------------------------------
    22. 0 - output([t1.c1], [t1.c2]), filter(nil)
    23. 1 - output([t1.c1], [t1.c2]), filter(nil)
    24. 2 - output([t1.c1], [t1.c2]), filter([t1.c1 < 150], [t1.c1 > 110]),
    25. access([t1.c1], [t1.c2]), partitions(p[0-1])

    如果查询条件是等值条件,则可以进行分区裁剪。示例如下:

    二级分区裁剪的基本原理

    对于二级分区,先按照一级分区键确定一级需要访问的分区,然后在通过二级分区键确定二级分区需要访问的分区。然后做一个乘积确定二级分区访问的所有物理分区。

    1. obclient> CREATE TABLE t1
    2. (
    3. c1 INT ,
    4. c2 INT
    5. )
    6. PARTITION BY hash(c1)
    7. SUBPARTITION BY RANGE(c2)
    8. SUBPARTITION sp0 VALUES less than(100),
    9. SUBPARTITION sp1 VALUES less than(200)
    10. ) partitions 5
    11. SELECT * FROM t1
    12. WHERE
    13. (c1 = 1 OR c1 = 2) AND
    14. (c2 > 101 AND c2 < 150)
    15. obclient> EXPLAIN SELECT * FROM t1 WHERE (c1 = 1 or c1 = 2) and (c2 > 101 and c2 < 150) \G
    16. *************************** 1. row ***************************
    17. Query Plan: ============================================
    18. |ID|OPERATOR |NAME|EST. ROWS|COST|
    19. --------------------------------------------
    20. |0 |EXCHANGE IN DISTR | |1 |1403|
    21. |1 |EXCHANGE OUT DISTR| |1 |1303|
    22. |2 | TABLE SCAN |t1 |1 |1303|
    23. ============================================
    24. Outputs & filters:
    25. -------------------------------------
    26. 0 - output([t1.c1], [t1.c2]), filter(nil)
    27. 1 - output([t1.c1], [t1.c2]), filter(nil)
    28. access([t1.c1], [t1.c2]), partitions(p1sp1, p2sp1)

    某些场景下,分区裁剪可能会存在一定程度的放大,但优化器可以确保裁剪的结果是所需访问数据的超集,不会存在丢失数据的情况。