POLARDB for Oracle 的查询规划器使用分区修剪 计算出高效计划来查找与 SELECT 语句的 WHERE 子句中指定的条件匹配的一行(或多行)。要通过执行计划成功修剪分区,WHERE 子句必须约束与创建已分区表时所指定的分区键列比较的信息。在查询:

  • 按列表分区的表时,分区修剪在 WHERE 子句使用运算符(如等于 (=) 或 AND)将文本值与分区键进行比较时有效。
  • 按范围分区的表时,分区修剪在 WHERE 子句使用运算符(如等于 (=)、小于 (<) 或大于 (>))将文本值与分区键进行比较时有效。
  • 按哈希分区的表时,分区修剪在 WHERE 子句使用运算符(如等于 (=))将文本值与分区键进行比较时有效。

分区修剪机制使用两种优化技术:

  • 快速修剪
  • 约束排除

分区修剪技术将数据搜索限制为要搜索的值可能位于的那些分区。这两种修剪技术从查询的执行计划中删除了分区,从而提高了性能。

快速修剪和约束排除之间的区别是快速修剪了解 Oracle 已分区表中的各分区之间的关系,而约束排除不了解。例如,当查询在按列表分区的表中搜索特定值时,快速修剪可以推断仅特定分区可能保存该值,而约束排除必须检查为每个 分区定义的约束。快速修剪发生在规划过程的早期以减少规划器必须考虑的分区数,而约束排除发生在规划过程的晚期。

使用约束排除

constraint_exclusion 参数控制约束排除。constraint_exclusion 参数可以具有值 on、off 或 partition。要启用约束排除,参数必须设置为 partition 或 on。默认情况下,参数设置为 partition。

有关约束排除的更多信息,请参见:https://www.postgresql.org/docs/11/static/ddl-partitioning.html

启用约束排除后,服务器将检查为每个分区定义的约束,以确定该分区是否可以满足查询。

在执行不 包含 WHERE 子句的 SELECT 语句时,查询规划器必须推荐搜索整个表的执行计划。在执行确实 包含 WHERE 子句的 SELECT 语句时,查询规划器确定行会存储在哪个分区中,并向该分区发送查询片段,通过执行计划修剪可能不包含该行的分区。如果没有使用已分区表,则禁用约束排除可以提高性能。

快速修剪

与约束排除一样,快速修剪只能优化包括 WHERE(或联接)子句的查询,并且仅在 WHERE 子句中的限定符与特定形式匹配时才能进行这种优化。在这两种情况下,查询规划器将避免在不可能保存查询所需数据的分区中搜索数据。

快速修剪由名为 edb_enable_pruning 的布尔配置参数控制。如果 edb_enable_pruning 为 ON,POLARDB for Oracle 将快速修剪特定查询。如果 edb_enable_pruning 为 OFF,服务器将禁用快速修剪。

说明 快速修剪无法优化对已二级分区表的查询或优化对按多个列进行分区的按范围分区的表的查询。

对于按列表分区的表,POLARDB for Oracle 可以快速修剪包含将分区依据列约束为文本值的 WHERE 子句的查询。例如,如下按列表分区的表:

CREATE TABLE sales_hist(..., country text, ...) PARTITION BY LIST(country)
(     PARTITION americas VALUES('US', 'CA', 'MX'),     PARTITION europe VALUES('BE', 'NL', 'FR'),     PARTITION asia VALUES('JP', 'PK', 'CN'),     PARTITION others VALUES(DEFAULT))

快速修剪可以推断 WHERE 子句,例如:

WHERE country = 'US'
WHERE country IS NULL;

对于第一个 WHERE 子句,快速修剪将消除分区 europe、asia 和 others,因为这些分区无法保存满足限定符 WHERE country = 'US' 的行。

对于第二个 WHERE 子句,快速修剪将消除分区 americas、europe 和 asia,因为这些分区无法保存 country IS NULL 的行。

WHERE 子句中指定的运算符必须为等号 (=) 或适用于分区依据列的数据类型的等于运算符。

对于按范围分区的表,POLARDB for Oracle 可以快速修剪包含将分区依据列约束为文本值的 WHERE 子句的查询,但运算符可以是以下任意一种:>>==<=<

快速修剪还将推断包含 AND 和 BETWEEN 运算符的更复杂的表达式,例如:

WHERE size > 100 AND size <= 200WHERE size BETWEEN 100 AND 200

但不能根据包含 OR 或 IN 的表达式进行修剪。

例如,当查询按范围分区的表时,例如:

CREATE TABLE boxes(id int, size int, color text)    PARTITION BY RANGE(size)(     PARTITION small VALUES LESS THAN(100),     PARTITION medium VALUES LESS THAN(200),     PARTITION large VALUES LESS THAN(300))

快速修剪可以推断 WHERE 子句,例如:

WHERE size > 100     -- scan partitions 'medium' and 'large'
WHERE size >= 100    -- scan partitions 'medium' and 'large'
WHERE size = 100     -- scan partition 'medium'
WHERE size <= 100    -- scan partitions 'small' and 'medium'
WHERE size < 100     -- scan partition 'small'
WHERE size > 100 AND size < 199     -- scan partition 'medium'
WHERE size BETWEEN 100 AND 199      -- scan partition 'medium'
WHERE color = 'red' AND size = 100  -- scan 'medium'
WHERE color = 'red' AND (size > 100 AND size < 199) -- scan 'medium'

在每种情况下,快速修剪都要求限定符必须引用分区依据列和文本值(或 IS NULL/IS NOT NULL)。

请注意,快速修剪还可以优化包含上述形式的 WHERE 子句的 DELETE 和 UPDATE 语句。

示例-分区修剪

EXPLAIN 语句显示语句的执行计划。您可以使用 EXPLAIN 语句来确认 POLARDB for Oracle 通过查询的执行计划修剪分区。

为演示分区修剪的效率,首先创建一个简单的表:

CREATE TABLE sales
(
  dept_no     number,
  part_no     varchar2,
  country     varchar2(20),
  date        date,
  amount      number
)
PARTITION BY LIST(country)
(
  PARTITION europe VALUES('FRANCE', 'ITALY'),
  PARTITION asia VALUES('INDIA', 'PAKISTAN'),
  PARTITION americas VALUES('US', 'CANADA')
);

然后,执行包括 EXPLAIN 语句的约束查询:

EXPLAIN (COSTS OFF) SELECT * FROM sales WHERE country = 'INDIA';

生成的查询计划显示,服务器将仅扫描 sales_asia 表,即 country 值为 INDIA 的行将存储在其中的表:

edb=# EXPLAIN (COSTS OFF) SELECT * FROM sales WHERE country = 'INDIA';
                    QUERY PLAN
---------------------------------------------------
 Append
   ->  Seq Scan on sales_asia
         Filter: ((country)::text = 'INDIA'::text)
(3 rows)

如果所执行查询搜索与未包括在分区键中的值匹配的行:

EXPLAIN (COSTS OFF) SELECT * FROM sales WHERE dept_no = '30';

生成的查询计划显示,服务器必须查看所有分区来查找满足查询的行:

edb=# EXPLAIN (COSTS OFF) SELECT * FROM sales WHERE dept_no = '30';
                QUERY PLAN
-------------------------------------------
 Append
   ->  Seq Scan on sales_americas
         Filter: (dept_no = '30'::numeric)
   ->  Seq Scan on sales_europe
         Filter: (dept_no = '30'::numeric)
   ->  Seq Scan on sales_asia
         Filter: (dept_no = '30'::numeric)
(7 rows)

在查询已二级分区表时,约束排除也适用:

CREATE TABLE sales
(
  dept_no     number,
  part_no     varchar2,
  country     varchar2(20),
  date        date,
  amount      number
)
PARTITION BY RANGE(date) SUBPARTITION BY LIST (country)
(   PARTITION "2011" VALUES LESS THAN('01-JAN-2012')
  (
    SUBPARTITION europe_2011 VALUES ('ITALY', 'FRANCE'),
    SUBPARTITION asia_2011 VALUES ('PAKISTAN', 'INDIA'),
    SUBPARTITION americas_2011 VALUES ('US', 'CANADA')
  ),
  PARTITION "2012" VALUES LESS THAN('01-JAN-2013')
  (
    SUBPARTITION europe_2012 VALUES ('ITALY', 'FRANCE'),
    SUBPARTITION asia_2012 VALUES ('PAKISTAN', 'INDIA'),
    SUBPARTITION americas_2012 VALUES ('US', 'CANADA')
  ),   PARTITION "2013" VALUES LESS THAN('01-JAN-2015')
  (
    SUBPARTITION europe_2013 VALUES ('ITALY', 'FRANCE'),
    SUBPARTITION asia_2013 VALUES ('PAKISTAN', 'INDIA'),
    SUBPARTITION americas_2013 VALUES ('US', 'CANADA')
  )
);

在查询表时,查询规划器将从搜索路径中修剪不可能包含所需结果集的任何分区或二级分区:

edb=# EXPLAIN (COSTS OFF) SELECT * FROM sales WHERE country = 'US' AND date = 'Dec 12, 2012';
                                                   QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
 Append
   ->  Seq Scan on sales_americas_2012
         Filter: (((country)::text = 'US'::text) AND (date = '12-DEC-12 00:00:00'::timestamp without time zone))
(3 rows)