GPORCA特性和增强

GPORCA是Greenplum的下一代查询优化器,它包括了对特定类型的查询和操作的增强:

GPORCA还包括下面这些优化增强:

  • 改进的连接排序
  • 连接-聚集重排序
  • 排序顺序优化
  • 查询优化中包括的数据倾斜估计

Parent topic: 关于GPORCA

对分区表的查询

GPORCA包括这些对分区表上查询的增强:

  • 改进了分区消除。
  • 支持统一多级分区表。有关统一多级分区表的信息,请见关于统一多级分区表
  • 查询计划可以包含分区选择器操作符。
  • 不在EXPLAIN计划中枚举分区。

    对于涉及静态分区选择的查询,在其中会将分区键与常量进行比较,GPORCA会在EXPLAIN输出的分区选择器操作符下面列出要被扫描的分区数。这个示例分区选择器操作符展示了过滤条件和选中的分区数:

    1. Partition Selector for Part_Table (dynamic scan id: 1)
    2. Filter: a > 10
    3. Partitions selected: 1 (out of 3)

    对于涉及动态分区选择的查询,在其中会将分区键与变量进行比较,要扫描的分区数只有在查询执行时才能知道。选中的分区不会显示在EXPLAIN输出中。

  • 计划尺寸与分区数无关。

  • 由于分区数导致的内存不足错误被减少。

这个CREATE TABLE命令的例子创建一个范围分区表。

  1. CREATE TABLE sales(order_id int, item_id int, amount numeric(15,2),
  2. date date, yr_qtr int)
  3. range partitioned by yr_qtr;

GPORCA改进了针对分区表的这些类型的查询:

  • 全表扫描。计划中不会枚举分区。

    1. SELECT * FROM sales;
  • 带一个常量过滤谓词的查询。会执行分区消除。

    1. SELECT * FROM sales WHERE yr_qtr = 201501;
  • 范围选择。会执行分区消除。

    1. SELECT * FROM sales WHERE yr_qtr BETWEEN 201601 AND 201704 ;
  • 涉及到分区表的连接。在下面这个例子中,分区过的维度表date_dim 被连接到事实表catalog_sales

    1. SELECT * FROM catalog_sales
    2. WHERE date_id IN (SELECT id FROM date_dim WHERE month=12);

含有子查询的查询

GPORCA更有效地处理子查询。子查询是嵌套在外层查询块里面的查询。在下面的查询中,WHERE子句中的SELECT是一个子查询。

  1. SELECT * FROM part
  2. WHERE price > (SELECT avg(price) FROM part);

GPORCA还能更有效地处理含有相关子查询(CSQ)的查询。相关子查询是使用来自外层查询的值的子查询。在下面的查询中,price列被使用在外层查询和子查询中。

  1. SELECT * FROM part p1
  2. WHERE price > (SELECT avg(price) FROM part p2
  3. WHERE p2.brand = p1.brand);

GPORCA为下列类型的子查询生成更有效的计划:

  • SELECT列表中的CSQ。

    1. SELECT *,
    2. (SELECT min(price) FROM part p2 WHERE p1.brand = p2.brand)
    3. AS foo
    4. FROM part p1;
  • 析取(OR)过滤条件中的CSQ。

    1. SELECT FROM part p1 WHERE p_size > 40 OR
    2. p_retailprice >
    3. (SELECT avg(p_retailprice)
    4. FROM part p2
    5. WHERE p2.p_brand = p1.p_brand)
  • 具有越级关联的嵌套CSQ

    1. SELECT * FROM part p1 WHERE p1.p_partkey
    2. IN (SELECT p_partkey FROM part p2 WHERE p2.p_retailprice =
    3. (SELECT min(p_retailprice)
    4. FROM part p3
    5. WHERE p3.p_brand = p1.p_brand)
    6. );

    Note: 传统查询优化器不支持具有越级关联的嵌套CSQ。

  • 有聚集和不等于的CSQ。下面这个例子包含一个有不等于的CSQ。

    1. SELECT * FROM part p1 WHERE p1.p_retailprice =
    2. (SELECT min(p_retailprice) FROM part p2 WHERE p2.p_brand <> p1.p_brand);
  • 必须返回一行的CSQ。

    1. SELECT p_partkey,
    2. (SELECT p_retailprice FROM part p2 WHERE p2.p_brand = p1.p_brand )
    3. FROM part p1;

含有公共表表达式的查询

GPORCA处理含有WITH子句的查询。WITH子句也被称为公共表表达式(CTE),它会生成只在该查询中存在的临时表。下面这个查询例子包含一个CTE。

  1. WITH v AS (SELECT a, sum(b) as s FROM T where c < 10 GROUP BY a)
  2. SELECT *FROM v AS v1 , v AS v2
  3. WHERE v1.a <> v2.a AND v1.s < v2.s;

作为查询优化的一部分,GPORCA可以把谓词下推到一个CTE中。对于下面的例子查询,GPORCA把等于谓词推到了CTE。

  1. WITH v AS (SELECT a, sum(b) as s FROM T GROUP BY a)
  2. SELECT *
  3. FROM v as v1, v as v2, v as v3
  4. WHERE v1.a < v2.a
  5. AND v1.s < v3.s
  6. AND v1.a = 10
  7. AND v2.a = 20
  8. AND v3.a = 30;

GPORCA可以处理这些类型的CTE:

  • 定义一个或者多个表的CTE。在下面的这个查询中,CTE定义两个表。

    1. WITH cte1 AS (SELECT a, sum(b) as s FROM T
    2. where c < 10 GROUP BY a),
    3. cte2 AS (SELECT a, s FROM cte1 where s > 1000)
    4. SELECT *
    5. FROM cte1 as v1, cte2 as v2, cte2 as v3
    6. WHERE v1.a < v2.a AND v1.s < v3.s;
  • 嵌套CTE。

    1. WITH v AS (WITH w AS (SELECT a, b FROM foo
    2. WHERE b < 5)
    3. SELECT w1.a, w2.b
    4. FROM w AS w1, w AS w2
    5. WHERE w1.a = w2.a AND w1.a > 2)
    6. SELECT v1.a, v2.a, v2.b
    7. FROM v as v1, v as v2
    8. WHERE v1.a < v2.a;

GPORCA的DML操作增强

GPORCA含有对DML操作(例如INSERT,UPDATE和DELETE)的增强。

  • 查询计划中的DML节点是一个查询计划操作符。
    • 可以作为一个常规节点(目前只有顶层切片)出现在计划中的任何地方
    • 可以有消费者
  • UPDATE操作使用查询计划操作符Split并且支持这些操作:

    • 表的分布键列上的UPDATE操作。
    • 表的分区键列上的UPDATE操作。

    这个计划的例子展示了Split操作符。

    1. QUERY PLAN
    2. --------------------------------------------------------------
    3. Update (cost=0.00..5.46 rows=1 width=1)
    4. -> Redistribute Motion 2:2 (slice1; segments: 2)
    5. Hash Key: a
    6. -> Result (cost=0.00..3.23 rows=1 width=48)
    7. -> Split (cost=0.00..2.13 rows=1 width=40)
    8. -> Result (cost=0.00..1.05 rows=1 width=40)
    9. -> Seq Scan on dmltest
  • 新的查询计划操作符Assert被用于约束检查。

    这个计划的例子展示了Assert操作符。

    1. QUERY PLAN
    2. ------------------------------------------------------------
    3. Insert (cost=0.00..4.61 rows=3 width=8)
    4. -> Assert (cost=0.00..3.37 rows=3 width=24)
    5. Assert Cond: (dmlsource.a > 2) IS DISTINCT FROM
    6. false
    7. -> Assert (cost=0.00..2.25 rows=3 width=24)
    8. Assert Cond: NOT dmlsource.b IS NULL
    9. -> Result (cost=0.00..1.14 rows=3 width=24)
    10. -> Seq Scan on dmlsource