并行查询Hint语法

Parallel Hints可以指定优化器是否选择并行执行,还支持指定并行度以及需要并行的表,以及各个算子的并行执行方式。PolarDB MySQL版8.0.1版本目前支持在并行查询中使用PARALLELNO_PARALLEL两种Hints。PolarDB MySQL版8.0.2版本除了支持PARALLELNO_PARALLEL外,还增加了PQ_DISTRIBUTE hint来控制join的并行策略;通过PQ_GROUPBYPQ_DISTINCTPQ_WINDOWPQ_ORDERBY hint来控制各个分析型算子的并行执行方式。

开启或关闭并行查询

  • 开启并行查询

    您可以使用如下任意一种方式开启并行查询:

    • SELECT /*+PARALLEL(x)*/ ... FROM ...;   -- x >0
    • SELECT /*+ SET_VAR(max_parallel_degree=n) */  *  FROM ...   -- n > 0
  • 关闭并行查询

    您可以使用如下任意一种方式关闭并行查询:

    • SELECT /*+NO_PARALLEL()*/ ... FROM ...;
    • SELECT /*+ SET_VAR(max_parallel_degree=0) */  *  FROM ...

通过Hint指定并行表

并行查询提供了PARALLELNO_PARALLEL两种Hint,可以指定允许哪些表并行扫描,不允许哪些表并行扫描。其中:

  • 通过PARALLEL Hint可以强制查询并行执行,同时可以指定并行度和并行扫描的表。语法如下所示:

    /*+ PARALLEL [( [query_block] [table_name]  [degree] )] */
  • 通过NO_PARALLEL Hint可以强制查询串行执行,或者指定不选择某些表作为并行扫描的表。

    /*+ NO_PARALLEL [( [query_block] [table_name][, table_name] )] */

其中参数说明如下所示

参数

说明

query_block

应用Hint的query block名称。

table_name

应用Hint的表名称。

degree

并行度。

示例:

SELECT /*+PARALLEL()*/ * FROM t1, t2; 
-- 当表记录数小于records_threshold_for_parallelism设置的行数 ( 默认10000行)时,会强制并行,
-- 并行度用系统默认max_parallel_degree, 如果max_parallel_degree > 0, 
-- 则打开并行,如果max_parallel_degree等于0时,依旧时关闭并行。

SELECT /*+PARALLEL(8)*/ * FROM t1, t2; 
-- 强制并行度8并行执行, 
-- 当表记录数小于records_threshold_for_parallelism设置的行数 ( 默认10000行)时,会强制并行,
-- 并行度设置max_parallel_degree为8。

SELECT /*+ SET_VAR(max_parallel_degree=8) */  *  FROM ...   
-- 设置并行度max_parallel_degree为8, 
-- 当表记录数小于records_threshold_for_parallelism设置的行数(如20000行)时,会自动关闭并行。

SELECT /*+PARALLEL(t1)*/ * FROM t1, t2; 
-- 选择t1表并行, 对t1表执行/*+PARALLEL()*/ 语法

SELECT /*+PARALLEL(t1 8)*/ * FROM t1, t2; 
-- 强制并行度8且选择t1表并行执行,  对t1表执行/*+PARALLEL(8)*/语法

SELECT /*+PARALLEL(@subq1)*/ SUM(t.a) FROM t WHERE t.a = 
  (SELECT /*+QB_NAME(subq1)*/ SUM(t1.a) FROM t1); 
--强制subquery并行执行, 并行度用系统默认max_parallel_degree, 
-- 如果max_parallel_degree > 0, 则打开并行,max_parallel_degree等于0时,依旧时关闭并行

SELECT /*+PARALLEL(@subq1 8)*/ SUM(t.a) FROM t WHERE t.a = 
  (SELECT /*+QB_NAME(subq1)*/ SUM(t1.a) FROM t1); 
--强制subquery并行执行, 并行度设置max_parallel_degree为8

SELECT SUM(t.a) FROM t WHERE t.a = 
  (SELECT /*+PARALLEL()*/ SUM(t1.a) FROM t1); 
--强制subquery并行执行, 
-- 并行度用系统默认max_parallel_degree, 
-- 如果max_parallel_degree > 0, 则打开并行,max_parallel_degree等于0时,依旧时关闭并行

SELECT SUM(t.a) FROM t WHERE t.a = 
  (SELECT /*+PARALLEL(8)*/ SUM(t1.a) FROM t1); 
--强制subquery并行执行, 设置并行度max_parallel_degree为8

SELECT /*+NO_PARALLEL()*/ * FROM t1, t2; 
-- 禁止并行执行

SELECT /*+NO_PARALLEL(t1)*/ * FROM t1, t2; 
-- 只对t1表禁止并行, 当系统打开并行时, 有可能对t2进行并行扫描,并行执行

SELECT /*+NO_PARALLEL(t1, t2)*/ * FROM t1, t2; 
-- 同时对t1和t2表禁止并行

SELECT /*+NO_PARALLEL(@subq1)*/ SUM(t.a) FROM t WHERE t.a = 
  (SELECT /*+QB_NAME(subq1)*/ SUM(t1.a) FROM t1); 
--禁止subquery并行执行

SELECT SUM(t.a) FROM t WHERE t.a = 
  (SELECT /*+NO_PARALLEL()*/ SUM(t1.a) FROM t1); 
 --禁止subquery并行执行
说明

对于不支持并行的查询或者并行扫描的表,PARALLEL Hint不生效。

并行Join

通过PQ_DISTRIBUTE hint可以指定join操作以哪种方式来执行并行查询。

  • 通过PQ_DISTRIBUTE指定某个表的数据分发方式:

    /*+ PQ_DISTRIBUTE([query_block] table_name  strategy ) */

    其中参数说明如下所示

    参数

    说明

    query_block

    应用Hint的query block名称。

    table_name

    应用Hint的表名称。

    strategy

    数据分发策略,包括:

    • PQ_GATHER:数据汇总到上层1个worker中。

    • PQ_HASH:数据shuffle分发到上层多个worker中。

    • PQ_BROADCAST: 数据广播到上层多个worker中。

    • PQ_NONE:不做数据分发。

    示例:

    SELECT /*+ PARALLEL(t1) PQ_DISTRIBUTE(t1 PQ_GATHER) */ * FROM t as t1;
    -- 当表记录数小于records_threshold_for_parallelism设置的行数( 默认10000行)时,会强制并行,
    -- 并行度用系统默认max_parallel_degree, 如果max_parallel_degree > 0,
    -- 则打开并行,如果max_parallel_degree等于0时,依旧关闭并行。
    -- 并行扫表后,不做数据分发,结果汇总到Leader
    SELECT /*+ PARALLEL(t1) PQ_DISTRIBUTE(t1 PQ_HASH) */t1.a, SUM(t1.b) * FROM t as t1 GROUP BY t1.a;
    -- 并行扫表后,将数据按照group by key分发到下层worker
  • 通过PQ_DISTRIBUTE指定两表的join方式:

    /*+ PQ_DISTRIBUTE([query_block] table_name strategy1 [strategy2]) */

    以上hint如果只指定了strategy1,则相当于指定某个表的数据分发方式;如果同时指定了strategy1strategy2,则用来指定table_name表和其前面一张表的并行join方式。

    说明

    table_name前面的表可以是一张物理表,或者是前面join的中间结果表。

    示例:

    SELECT /*+ PARALLEL(t1) PARALLEL(t2) PQ_DISTRIBUTE(t2 PQ_HASH PQ_HASH) */ * FROM t as t1 STRAIGHT_JOIN t as t2 on t1.b = t2.c;
    -- 在t1表上做并行扫表,然后将数据做shuffle分发到下一阶段Workers
    -- 在t2表上做并行扫表,然后将数据做shuffle分发到下一阶段Workers
    -- 在下阶段Workers上完成co-location join后,结果汇总到Leader
    SELECT /*+ PARALLEL(t1) PARALLEL(t2) PQ_DISTRIBUTE(t2 PQ_GATHER PQ_GATHER) */ * FROM t as t1 STRAIGHT_JOIN t as t2 on t1.b = t2.c;
    -- 在t1表上做并行扫表,然后将数据做汇总到Leader
    -- 在t2表上做并行扫表,然后将数据做汇总到Leader
    -- 在Leader上流水线收集数据,完成join操作

    对于不支持并行的查询或者相互矛盾的hint,可能会导致查询无法并行。示例如下:

    SELECT /*+ PARALLEL(t1) PARALLEL(t2) PQ_DISTRIBUTE(t2 PQ_HASH PQ_GATHER) */ * FROM t as t1 STRAIGHT_JOIN t as t2 on t1.b = t2.c;
    -- 在t1表上做并行扫表,然后将数据分发到下一层多个Worker上
    -- 在t2表上做并行扫表,然后将数据做汇总到Leader
    -- 两个hint在数据分布方式矛盾,会导致无法生成并行计划

并行分组聚集

通过PQ_GROUPBY指定分组聚集的执行方式:

/*+ PQ_GROUPBY(strategy) */

其中参数说明如下所示:

参数

说明

strategy

数据分发策略,包括:

  • PQ_ONEPHASE:数据根据分组键分发到多个worker中,一次性并行完成分组聚集操作。

  • PQ_TWOPHASE_GATHER:数据在现有worker中完成局部聚集,再汇总到Leader上完成最终汇总。

  • PQ_TWOPHASE_HASH:数据在现有worker中完成局部聚集,再根据分组键分发到下阶段多个worker中。

  • PQ_SERIAL:串行完成分组聚集。

示例:

SELECT /*+ PARALLEL(t1) PQ_GROUPBY(PQ_ONEPHASE) */ t1.a, sum(t1.b) FROM t as t1 GROUP BY t1.a;
-- 在t1表上做并行扫表,然后将数据按照t1.a列分发到下一层多个Worker上
-- 在下一阶段多个Worker中完成聚集计算,结果汇总到Leader

SELECT /*+ PARALLEL(t1) PQ_GROUPBY(PQ_TWOPHASE_HASH) */ t1.a, sum(t1.b) FROM t as t1 GROUP BY t1.a;
-- 在t1表上做并行扫表,然后直接在扫表的Workers上,完成一阶段的聚集操作
-- 将中间聚集结果按照t1.a列分发到下一层多个Worker上
-- 在下一阶段多个Worker中完成最终聚集计算,结果汇总到Leader

并行DISTINCT

通过PQ_DISTINCT指定分组聚集的执行方式:

/*+ PQ_DISTINCT(strategy) */

其中参数说明如下所示:

参数

说明

strategy

数据分发策略,包括:

  • PQ_TWOPHASE_GATHER:数据在现有worker中完成局部去重,再汇总到Leader上完成最终去重。

  • PQ_SERIAL:串行完成分组聚集。

并行窗口函数

通过PQ_WINDOW指定窗口函数的执行方式:

/*+ PQ_WINDOW([window_name] strategy) */
说明

带窗口名称hint的优先级高于没有名称的hint。

其中参数说明如下所示:

参数

说明

window_name

窗口函数名称。指定策略对哪个窗口函数生效。如果不设置,默认对所有窗口函数生效。

strategy

数据分发策略,包括:

  • PQ_ONEPHASE:基于Partition by子句做数据分发,然后在Workers上并行完成窗口计算。

  • PQ_SERIAL:串行完成分组聚集。

示例:

SELECT /*+ PQ_WINDOW(PQ_ONEPHASE)  PQ_WINDOW(win PQ_SERIAL) */
  ROW_NUMBER() OVER(win) AS 'row_number',
  RANK() over(partition by name order by salary desc)
FROM employee_salaries WINDOW win as (partition by dept order by salary desc);
-- 对于名称为win的窗口函数,通过串行方式计算
-- 对于其他窗口函数,通过在partition by key上分布做并行计算

并行Order by

通过PQ_ORDERBY指定排序操作的执行方式:

/*+ PQ_ORDERBY(strategy) */

其中参数说明如下所示:

参数

说明

strategy

数据分发策略,包括:

  • PQ_TWOPHASE_GATHER:数据在现有worker中完成局部排序,再汇总到Leader上完成最终排序。

  • PQ_SERIAL:串行完成排序。

并行子查询

并行子查询的选择方式(并行子查询详细信息请参见子查询支持)也可以通过Hint来进行控制,语法及说明如下:

/*+ PQ_PUSHDOWN [( [query_block])] */      #对应的子查询会选择push down的并行子查询执行策略。
/*+ NO_PQ_PUSHDOWN [( [query_block])] */   #对应的子查询会选择shared access的并行子查询执行策略。

示例:

#子查询选择push down并行策略
EXPLAIN SELECT /*+ PQ_PUSHDOWN(@qb1) */ * FROM t2 WHERE t2.a =
                 (SELECT /*+ qb_name(qb1) */ a FROM t1);

#子查询选择shared access并行策略
EXPLAIN SELECT /*+ NO_PQ_PUSHDOWN(@qb1) */ * FROM t2 WHERE t2.a =
                 (SELECT /*+ qb_name(qb1) */ a FROM t1);
#不加query block进行控制
EXPLAIN SELECT * FROM t2 WHERE t2.a =
                 (SELECT /*+ NO_PQ_PUSHDOWN() */ a FROM t1);