概述
TDSQL 在兼容 MySQL 官方 Hint 标准的基础上,针对分布式并行执行特性扩展了专门的并行 Hint。这些 Hint 主要用于优化器指导,帮助用户更精细地控制查询的并行执行策略,提升复杂查询的性能。
PARALLEL/NO_PARALLEL Hint 并行度控制
可以使用 PARALLEL Hint 指定进行 Parallel Scan 的表和它的并行度。语法如下:

PARALLEL Hint 是一个3个层级的 Hint Global、Query Block 和 Table 级别,举例说明:
PARALLEL(num) -- Global级、Query Block级PARALLEL(@qbname num) -- Query Block级PARALLEL(tablename) -- Table 级PARALLEL(tablename num) -- Table 级NO_PARALLEL -- Global级、Query Block级NO_PARALLEL(@qbname) -- Query Block级NO_PARALLEL(tablename) -- Table 级
其中,NO_PARALLEL 是禁止并行,tablename 和 qbname 的语法请参考 MySQL 官方文档。
Global 级:当在主查询中指定,没有给定 query block name 和 table name 时。这时 num 将作为整个查询的默认并行度,子查询还可以通过自己的PARALLEL Hint 来覆盖它。
Query Block 级:当在非主查询中指定,或者给定了 query block name,但没有给定 table name 时。这时 num 将作为这个子查询的默认并行度,可以被表级的覆盖。
Table 级:当在 Hint 中给定了表名时。如果只给表名没给 num,会向上(即:Query Block、Table 级别、max_parallel_degree 变量)确定并行度,如果最后没有拿到并行度,即到 max_parallel_degree 时该值为0,则不会并行。另外,我们可以支持指定多个表并行,将来的计划中会支持多个表并行。
这里 Query Block 级和 Global 级(当 Query Block 级未指定时)是可以当前决定当前 Query Block 是否使用并行计划的,如当它被指定了 NO_PARALLEL 时那么当时 Query Block 就不会进入并行优化阶段,而表级别只影响当前表是否进行并行扫描,也就是说如果 Query Block 和 Global 级别未指定 Hint,而当前的 max_parallel_degree 大于0,即使所有表都使用了 NO_PARALLEL,并行优化器还是会找出一个并行扫描表来并行执行。
针对 PARALLEL Hint 的语法,显示一些用法如下:
-- Global 级别EXPLAIN select /*+ PARALLEL(4) */ * from t1 where a > 4;-- Query block 级别,只在最顶层查询起作用EXPLAIN select /*+ QB_NAME(q1) PARALLEL(@q1 4) */ * from t1 where a > 4;-- Query block 级别,只在子查询中有效explain select * from t3 where a = (select /*+ PARALLEL(2) */ count(a) from t3);-- Table 级别EXPLAIN select /*+ PARALLEL(t1 4) */ from t1 where a > 4;-- Table 级别EXPLAIN select /*+ PARALLEL(t3@q2 4) */ * from t3 where a = (select /*+ QB_NAME(q2) */ count(a) from t3);-- Table 级别EXPLAIN select /*+ PARALLEL(@q2 t3 4) */ * from t3 where a = (select /*+ QB_NAME(q2) */ count(a) from t3);
PQ_DISTRIBUTE Hint 数据分布策略
用于指示优化器如何在查询计划中添加数据重分布操作。语法如下:

-- 基本语法PQ_DISTRIBUTE(tablespec strategy1 strategy2)PQ_DISTRIBUTE(tablespec strategy)-- 针对特定操作类型PQ_DISTRIBUTE(target strategy1 strategy2)
target :操作目标,支持 AGGREGATE、SORT 和 WINDOW,分别指定 GROUP BY,ORDER BY, WINDOW 函数的分布策略,WINDOW 可以在后面指定 WINDOW 的名字。strategy1、strategy2:重分布的策略,支持 NONE、GATHER、HASH、BROADCAST。JOIN 操作分布策略
PQ_DISTRIBUTE(t1 HASH, HASH)、PQ_DISTRIBUTE(t1@qb1 HASH, HASH) 或PQ_DISTRIBUTE(@qb1 t1 HASH HASH):指定 t1 和它前一个表做 JOIN 时,先使用 HASH 重分布,再在各个节点上做 JOIN。PQ_DISTRIBUTE(t1 BROADCAST, NONE):指定 t1 和它前一个表做 JOIN 时,前一个也就是外表做 Broadcast,再和 t1 做 JOIN。PQ_DISTRIBUTE(t1 GATHER):指定 t1 表先做 Gather,再和其他表做 JOIN。使用示例:
-- 两个表都进行 HASH 重分布后 JOINSELECT /*+ PQ_DISTRIBUTE(t1 HASH, HASH) */ *FROM table1 t1 JOIN table2 t2 ON t1.id = t2.id;-- 外表广播,内表不重分布SELECT /*+ PQ_DISTRIBUTE(t1 BROADCAST, NONE) */ *FROM small_table t1 JOIN large_table t2 ON t1.id = t2.id;-- 表数据收集到 Leader 后执行 JOINSELECT /*+ PQ_DISTRIBUTE(t1 GATHER) */ *FROM table1 t1 JOIN table2 t2 ON t1.id = t2.id;
GROUP BY 聚合分布策略
并行 GROUP BY 操作最多会执行二阶段,这里第1个策略表示是第一阶段 GROUP BY 之前的数据分发操作,第2个则表示第二阶段之前的数据分发操作,如果只有一个策略则表示是一阶段。
PQ_DISTRIBUTE(AGGREGATE NONE):完全下推一阶段。PQ_DISTRIBUTE(AGGREGATE NONE, GATHER):Worker 和 Leader 二阶段。PQ_DISTRIBUTE(AGGREGATE NONE HASH):Worker 和 Worker 二阶段。PQ_DISTRIBUTE(AGGREGATE GATHER):只在 Leader 上的一阶段。使用示例:
-- 完全下推的一阶段聚合SELECT /*+ PQ_DISTRIBUTE(AGGREGATE NONE) */department, AVG(salary)FROM employeesGROUP BY department;-- Worker 和 Leader 二阶段聚合SELECT /*+ PQ_DISTRIBUTE(AGGREGATE NONE, GATHER) */department, AVG(salary)FROM employeesGROUP BY department;-- Worker 和 Worker 二阶段聚合SELECT /*+ PQ_DISTRIBUTE(AGGREGATE NONE HASH) */department, AVG(salary)FROM employeesGROUP BY department;-- 只在 Leader 上执行的一阶段聚合SELECT /*+ PQ_DISTRIBUTE(AGGREGATE GATHER) */department, AVG(salary)FROM employeesGROUP BY department;
ORDER BY 排序分布策略
PQ_DISTRIBUTE(SORT NONE, GATHER):Worker + Leader 的 Merge Sort。PQ_DISTRIBUTE(SORT GATHER):只在 Leader 上做 Sort。使用示例:
-- Worker 局部排序 + Leader 归并排序SELECT /*+ PQ_DISTRIBUTE(SORT NONE, GATHER) */ *FROM large_tableORDER BY create_time DESC;-- 只在 Leader 上全局排序SELECT /*+ PQ_DISTRIBUTE(SORT GATHER) */ *FROM large_tableORDER BY create_time DESC;
WINDOW 函数分布策略
PQ_DISTRIBUTE(WINDOW GATHER)
策略说明: 将窗口函数计算完全集中在 Leader 节点执行。所有数据先收集到 Leader 节点,然后在单节点上执行窗口函数计算。
适用场景:
数据量相对较小,可以单节点处理
需要全局排序的窗口函数(如 RANK() OVER (ORDER BY ...))
窗口函数分区数较少,无法有效并行化
示例:
-- 在 Leader 节点上计算全局排名SELECT /*+ PQ_DISTRIBUTE(WINDOW GATHER) */student_id,score,RANK() OVER (ORDER BY score DESC) as global_rankFROM exam_scores;-- 计算每个学生的成绩变化趋势(数据量较小)SELECT /*+ PQ_DISTRIBUTE(WINDOW GATHER) */student_id,exam_date,score,LAG(score) OVER (PARTITION BY student_id ORDER BY exam_date) as prev_scoreFROM student_scores;
PQ_DISTRIBUTE(WINDOW HASH)
策略说明: 按照窗口函数的 PARTITION BY 子句的列进行哈希分布,在各个 Worker 节点上并行执行窗口函数计算。
适用场景:
数据量较大,需要并行处理
窗口函数有明确的分区键,且分区数据分布均匀
每个分区内的数据量适中,适合并行计算
示例:
-- 按学科分区并行计算排名SELECT /*+ PQ_DISTRIBUTE(WINDOW HASH) */subject,student_id,score,RANK() OVER (PARTITION BY subject ORDER BY score DESC) as subject_rankFROM exam_scores;-- 按部门分区计算工资累计总和SELECT /*+ PQ_DISTRIBUTE(WINDOW HASH) */department,employee_id,salary,SUM(salary) OVER (PARTITION BY department ORDER BY hire_date) as cumulative_salaryFROM employees;
PQ_DISTRIBUTE(WINDOW win1 GATHER)
策略说明: 针对特定命名的窗口(使用 WINDOW 子句定义)采用 GATHER 策略执行。
适用场景:
查询中包含多个窗口函数,需要对特定窗口采用不同策略
命名的窗口函数需要特殊处理
混合使用不同分布策略优化复杂查询
示例:
-- 为特定命名的窗口使用GATHER策略SELECT /*+ PQ_DISTRIBUTE(WINDOW win1 GATHER) */student_id,subject,score,AVG(score) OVER win1 as avg_score,RANK() OVER (PARTITION BY subject ORDER BY score DESC) as rankFROM exam_scoresWINDOW win1 AS (PARTITION BY student_id);-- 多个命名窗口的不同策略SELECT /*+ PQ_DISTRIBUTE(WINDOW win1 GATHER) PQ_DISTRIBUTE(WINDOW win2 HASH) */department,employee_id,salary,AVG(salary) OVER win1 as dept_avg,SUM(salary) OVER win2 as running_totalFROM employeesWINDOW win1 AS (PARTITION BY department),win2 AS (PARTITION BY department ORDER BY hire_date);
PQ_DISTRIBUTE(WINDOW win1 HASH)
策略说明: 针对特定命名的窗口采用 HASH 分布策略并行执行。
适用场景:
特定命名的窗口函数数据量较大,需要并行优化
不同窗口函数需要采用不同的并行策略
精细化控制复杂查询的执行计划
示例:
-- 为特定窗口使用 HASH 分布SELECT /*+ PQ_DISTRIBUTE(WINDOW win1 HASH) */product_category,product_id,sales_amount,SUM(sales_amount) OVER win1 as category_total,RANK() OVER (ORDER BY sales_amount DESC) as global_rankFROM sales_dataWINDOW win1 AS (PARTITION BY product_category);-- 混合策略:一个窗口 GATHER,另一个窗口 HASHSELECT /*+ PQ_DISTRIBUTE(WINDOW win1 GATHER) PQ_DISTRIBUTE(WINDOW win2 HASH) */customer_id,order_date,amount,AVG(amount) OVER win1 as cust_avg, -- 小数据量用GATHERSUM(amount) OVER win2 as running_sum -- 大数据量用HASHFROM ordersWINDOW win1 AS (PARTITION BY customer_id),win2 AS (PARTITION BY customer_id ORDER BY order_date);
SUBQUERY Hint 子查询并行策略
PQ_PRE_EVALUATION:指定子查询在引用它的父查询执行之前先提前执行,并行 Worker 之间直接读取子查询结果。
PQ_INLINE_EVALUATION:强制不提前执行子查询,也就是按 MySQL 的逻辑根据父查询的需要按需执行。
示例:
-- 指定这个 IN 子查询使用 MATERIALIZATION 策略并且在并行计划不使用预先执行策略EXPLAIN FORMAT=TREESELECT * FROM t1WHERE t1.a IN (SELECT /*+ SUBQUERY(MATERIALIZATION, PQ_INLINE_EVALUATION) */ aFROM t2);-- 指定 Derived Table 子查询 t 使用预先执行策略EXPLAIN FORMAT=TREESELECT /*+ NO_MERGE(t) pq_distribute(t1 none) */ t1.aFROM t1, (SELECT /*+ subquery(pq_inline_evaluation) */ aFROM t2) tWHERE t1.a = t.a;