Hint

最近更新时间:2025-12-24 12:03:43

我的收藏

概述

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 的名字。
strategy1strategy2:重分布的策略,支持 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 重分布后 JOIN
SELECT /*+ 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 后执行 JOIN
SELECT /*+ 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 employees
GROUP BY department;

-- Worker 和 Leader 二阶段聚合
SELECT /*+ PQ_DISTRIBUTE(AGGREGATE NONE, GATHER) */
department, AVG(salary)
FROM employees
GROUP BY department;

-- Worker 和 Worker 二阶段聚合
SELECT /*+ PQ_DISTRIBUTE(AGGREGATE NONE HASH) */
department, AVG(salary)
FROM employees
GROUP BY department;

-- 只在 Leader 上执行的一阶段聚合
SELECT /*+ PQ_DISTRIBUTE(AGGREGATE GATHER) */
department, AVG(salary)
FROM employees
GROUP 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_table
ORDER BY create_time DESC;

-- 只在 Leader 上全局排序
SELECT /*+ PQ_DISTRIBUTE(SORT GATHER) */ *
FROM large_table
ORDER 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_rank
FROM 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_score
FROM 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_rank
FROM exam_scores;

-- 按部门分区计算工资累计总和
SELECT /*+ PQ_DISTRIBUTE(WINDOW HASH) */
department,
employee_id,
salary,
SUM(salary) OVER (PARTITION BY department ORDER BY hire_date) as cumulative_salary
FROM 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 rank
FROM exam_scores
WINDOW 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_total
FROM employees
WINDOW 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_rank
FROM sales_data
WINDOW win1 AS (PARTITION BY product_category);

-- 混合策略:一个窗口 GATHER,另一个窗口 HASH
SELECT /*+ PQ_DISTRIBUTE(WINDOW win1 GATHER) PQ_DISTRIBUTE(WINDOW win2 HASH) */
customer_id,
order_date,
amount,
AVG(amount) OVER win1 as cust_avg, -- 小数据量用GATHER
SUM(amount) OVER win2 as running_sum -- 大数据量用HASH
FROM orders
WINDOW win1 AS (PARTITION BY customer_id),
win2 AS (PARTITION BY customer_id ORDER BY order_date);

SUBQUERY Hint 子查询并行策略

并行查询 MySQL SUBQUERY Hint 增加了2个并行相关的策略(strategy),这些策略可以和 MySQL 原来的策略混合使用。
PQ_PRE_EVALUATION:指定子查询在引用它的父查询执行之前先提前执行,并行 Worker 之间直接读取子查询结果。
PQ_INLINE_EVALUATION:强制不提前执行子查询,也就是按 MySQL 的逻辑根据父查询的需要按需执行。
示例:
-- 指定这个 IN 子查询使用 MATERIALIZATION 策略并且在并行计划不使用预先执行策略
EXPLAIN FORMAT=TREE
SELECT * FROM t1
WHERE t1.a IN (
SELECT /*+ SUBQUERY(MATERIALIZATION, PQ_INLINE_EVALUATION) */ a
FROM t2
);

-- 指定 Derived Table 子查询 t 使用预先执行策略
EXPLAIN FORMAT=TREE
SELECT /*+ NO_MERGE(t) pq_distribute(t1 none) */ t1.a
FROM t1, (
SELECT /*+ subquery(pq_inline_evaluation) */ a
FROM t2
) t
WHERE t1.a = t.a;