TDSQL Boundless 优化器 Hints

TDSQL Boundless 在兼容 MySQL Hint 标准的基础上,针对分布式并行执行特性扩展了专门的并行 Hint。这些 Hint 主要用于优化器指导,帮助用户更精细地控制查询的并行执行策略,提升复杂查询的性能。

相关产品
TDSQL Boundless 产品页,含版本规格、价格与购买入口。

二、PARALLEL/NO_PARALLEL Hint 并行度控制

PARALLEL Hint 用于指定进行并行扫描(Parallel Scan)的表、并行度和并行扫描方式。

PARALLEL Hint 是一个三层级的 Hint:Global、Query Block、Table。每个级别都可以指定可选的并行度(integer 或者 AUTO),NO_PARALLEL 用于禁止并行。MySQL 标准 Hint 语法请参见 Optimizer Hints。

注意:说明:

注意:PARALLEL HINT 中可以将 integer 替换为 AUTO,指定优化器自行计算最优的并行度。特别地,为了避免歧义,对于名为 AUTO 的表,在 PARALLEL HINT 中需要通过反引号(`)进行引用。

PARALLEL Hint 还支持在并行度后追加一个可选的扫描策略参数 parallel_scan_type,用于显式声明并行扫描的切分方式,方便在已知数据分布或访问路径特征时获得更稳定的并行执行计划。不指定该参数时由优化器自动选择。

三、三个级别的语义

级别 触发条件 行为
Global 级 在主查询中指定,且未给定 query block name 和 table name integer 作为整个查询的默认并行度。子查询可通过自身的 PARALLEL Hint 覆盖该值。
Query Block 级 在非主查询中指定,或显式给定 query block name 但未给定 table name integer 作为该子查询的默认并行度。
Table 级 Hint 中给定了表名 若仅给表名而未给 integer,则向上继承(依次为 Query Block 级、Global 级、max_parallel_degree 变量)。若最终未取得并行度(即 max_parallel_degree 为0),则不并行。

注意:说明:

四、示例

— Global 级别EXPLAIN SELECT /*+ PARALLEL(4) */ * FROM t1 WHERE a > 4;EXPLAIN SELECT /*+ PARALLEL(DYNAMIC_RANGE) */ * FROM t1 WHERE a > 4;EXPLAIN SELECT /*+ PARALLEL(4 PARTITION) */ * FROM t1 WHERE a > 4;EXPLAIN SELECT /*+ PARALLEL(t1 DYNAMIC_RANGE) */ * FROM t1 WHERE a > 4;EXPLAIN SELECT /*+ PARALLEL(AUTO) */ * FROM t1 WHERE a > 4;– Query Block 级别(仅在最顶层查询起作用)EXPLAIN SELECT /*+ QB_NAME(q1) PARALLEL(@q1 4) */ * FROM t1 WHERE a > 4;EXPLAIN SELECT /*+ QB_NAME(q1) PARALLEL(@q1 AUTO) */ * FROM t1 WHERE a > 4;– Query Block 级别(仅在子查询中有效)EXPLAIN SELECT * FROM t3 WHERE a = (SELECT /*+ PARALLEL(2) */ COUNT(a) FROM t3);EXPLAIN SELECT * FROM t3 WHERE a = (SELECT /*+ PARALLEL(AUTO) */ COUNT(a) FROM t3);– Table 级别EXPLAIN SELECT /*+ PARALLEL(t1 4) */ * FROM t1 WHERE a > 4;EXPLAIN SELECT /*+ PARALLEL(t3@q2 4) */ * FROM t3 WHERE a = (SELECT /*+ QB_NAME(q2) */ COUNT(a) FROM t3);EXPLAIN SELECT /*+ PARALLEL(@q2 t3 4) */ * FROM t3 WHERE a = (SELECT /*+ QB_NAME(q2) */ COUNT(a) FROM t3);EXPLAIN SELECT /*+ PARALLEL(`auto` 4) */ * FROM t1 WHERE a > 4; — 指定名为auto的表的并行度为4

PARALLEL Hint 与固定查询计划结合,可以在不修改语句的情况下,对特定语句开启并行:

CALL dbms_admin.statement_outline_add_rule('test', 'select /*+ PARALLEL(4) */ sum(c),b from t1 group by b');

五、并行扫描策略

PARALLEL Hint 的参数列表末尾可追加一个可选的 parallel_scan_type,用于声明并行扫描的切分策略。语法形式如下:

/*+ PARALLEL([@query_block | tbl_name] [integer] [parallel_scan_type]) *//*+ PARALLEL(parallel_scan_type) *//*+ PARALLEL(@query_block parallel_scan_type) */

parallel_scan_type 的可选取值如下表所示:

取值 适用对象 说明
DYNAMIC_RANGE 分区表、非分区表 基于索引或主键的连续范围进行动态切分,由各 Worker 在执行期动态获取尚未扫描的子区间。适用于数据量较大、访问路径为索引范围扫描或全表扫描的场景。
PARTITION 仅分区表 按分区切分,每个 Worker 负责一个或多个分区的扫描。实际并行度自动取并行度参数与分区数中的较小值。仅适用于分区表,对非分区表使用时优化器会自动忽略该策略。

注意:说明:

示例:

— Global 级:仅声明扫描策略,由优化器使用默认或会话级并行度EXPLAIN SELECT /*+ PARALLEL(DYNAMIC_RANGE) */ * FROM t1 WHERE a > 4;– Global 级:同时指定并行度与扫描策略EXPLAIN SELECT /*+ PARALLEL(4 DYNAMIC_RANGE) */ * FROM t1 WHERE a > 4;– Query Block 级:在指定查询块内启用按分区并行扫描EXPLAIN SELECT /*+ QB_NAME(q1) PARALLEL(@q1 4 PARTITION) */ *FROM t_partitioned WHERE created_date >= '2024-01-01';– Table 级:仅对分区表 t_part 启用按分区并行扫描EXPLAIN SELECT /*+ PARALLEL(t_part 8 PARTITION) */ *FROM t_part JOIN t1 ON t_part.id = t1.id;– Table 级:仅对表 t1 指定动态范围切分,并继承上层并行度EXPLAIN SELECT /*+ PARALLEL(t1 DYNAMIC_RANGE) */ * FROM t1 WHERE a > 4;

六、PQ_DISTRIBUTE Hint 数据分布策略

PQ_DISTRIBUTE 用于指示并行查询优化器如何在查询计划中添加数据重分布操作,以及 GROUP BY、ORDER BY 和窗口函数(WINDOW)的并行执行策略。语法如下:

七、dist_type 语义说明

·Table target(表目标)

写法 含义
PQ_DISTRIBUTE(t1 NONE) 单个 dist_type:控制本表分发。NONE = 不重分布(保持本地数据)
PQ_DISTRIBUTE(t1 GATHER) 单个 dist_type:控制本表分发。GATHER = 添加 collector(汇聚到 leader)
PQ_DISTRIBUTE(t1 d1, d2) 两个 dist_type:第一个 = 对端表(join 另一侧),第二个 = 本表。各取值同上

注意:说明:

注意:目前仅 NONE 和 GATHER 实际生效;HASH / BROADCAST 可解析但会被视为 unspecified

·Operation target(操作目标:AGGREGATE / SORT / WINDOW)

写法 含义
PQ_DISTRIBUTE(AGGREGATE GATHER) 单阶段聚合:先将所有数据 gather 到 leader,再做聚合
PQ_DISTRIBUTE(AGGREGATE NONE) 下推单阶段聚合:在各 worker 上直接聚合,不做数据重分布
PQ_DISTRIBUTE(AGGREGATE NONE, GATHER) 两阶段聚合:worker 做局部聚合 → gather → leader 做最终聚合
PQ_DISTRIBUTE(SORT GATHER) 不下推排序:先将所有数据 gather 到 leader,再做排序
PQ_DISTRIBUTE(SORT NONE, GATHER) 下推排序:worker 做局部排序 → gather → leader 做归并排序

八、语法

/*+ PQ_DISTRIBUTE([@query_block] target dist_type[, dist_type]) */

其中 target 有三种类型:

目标类型 语法形式 说明
表目标 PQ_DISTRIBUTE([@qb] tbl_name dist_type[, dist_type]) 控制表在 Join 中的数据分布方式
操作目标 PQ_DISTRIBUTE([@qb] AGGREGATE|SORT dist_type[, dist_type]) 控制聚合或排序操作的并行策略
命名窗口目标 PQ_DISTRIBUTE([@qb] WINDOW win_name dist_type[, dist_type]) 控制指定命名窗口函数的并行策略

·@qb 为可选的 query block 名称(如 @qb1),用于指定 Hint 作用的查询块。

·dist_type 可选值为:NONE、GATHER。其中 HASH 和 BROADCAST 目前未生效,会被视为未指定。

九、表目标语义

当 target 为表名时,Hint 控制该表在 Join 操作中的数据分布方式。

写法 含义
PQ_DISTRIBUTE(t1 NONE) 表 t1 不添加 Collector(数据留在各 Worker 上)
PQ_DISTRIBUTE(t1 GATHER) 表 t1 添加 Collector(数据汇聚到 Leader)
PQ_DISTRIBUTE(t1 d1, d2) d1 控制 JOIN 对端的分布方式,d2 控制本表(t1)的分布方式

当指定两个 dist_type 时,第一个参数 d1 作用于 JOIN 的对端表,第二个参数 d2 作用于本表。例如:

— t1 不添加 Collector(留在 Worker),Join 对端添加 Collector(汇聚到 Leader)SELECT /*+ PQ_DISTRIBUTE(t1 GATHER, NONE) */ * FROM t1 JOIN t2 ON t1.id = t2.id;

十、操作目标语义

当 target 为 AGGREGATE 或 SORT 时,Hint 控制聚合或排序操作的并行执行策略。

十一、聚合(AGGREGATE)

写法 含义
PQ_DISTRIBUTE(AGGREGATE GATHER) 单阶段聚合:各 Worker 数据汇聚到 Leader 后执行聚合
PQ_DISTRIBUTE(AGGREGATE NONE) 下推单阶段聚合:各 Worker 上直接执行完整聚合(无需重分布)
PQ_DISTRIBUTE(AGGREGATE NONE, GATHER) 两阶段聚合:各 Worker 先执行部分聚合,再汇聚到 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;– 只在 Leader 上执行的一阶段聚合SELECT /*+ PQ_DISTRIBUTE(AGGREGATE GATHER) */ department, AVG(salary) FROM employees GROUP BY department;

十二、排序(SORT)

写法 含义
PQ_DISTRIBUTE(SORT GATHER) 非下推排序:各 Worker 数据汇聚到 Leader 后执行排序
PQ_DISTRIBUTE(SORT NONE, GATHER) 下推排序:各 Worker 先执行局部排序,再汇聚到 Leader 执行归并排序

— 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;

十三、命名窗口目标语义

当 target 为 WINDOW win_name 时,Hint 控制指定命名窗口函数的并行策略,语义与 SORT 类似。

SELECT /*+ PQ_DISTRIBUTE(WINDOW w NONE, GATHER) */ SUM(val) OVER wFROM t1 WINDOW w AS (ORDER BY id);

示例

— 表目标:控制 t1 在 Join 中不添加 CollectorSELECT /*+ PQ_DISTRIBUTE(t1 NONE) */ * FROM t1 JOIN t2 ON t1.id = t2.id;– 表目标(指定 query block):t1 添加 Collector,对端不添加SELECT /*+ PQ_DISTRIBUTE(@qb1 t1 NONE, GATHER) */ * FROM t1 JOIN t2 ON t1.id = t2.id;– 操作目标:使用两阶段聚合SELECT /*+ PQ_DISTRIBUTE(AGGREGATE NONE, GATHER) */ dept, SUM(salary) FROM emp GROUP BY dept;– 操作目标:下推排序SELECT /*+ PQ_DISTRIBUTE(SORT NONE, GATHER) */ * FROM t1 ORDER BY col1;– 组合使用:同时指定表分布和聚合策略SELECT /*+ PQ_DISTRIBUTE(t1 NONE) PQ_DISTRIBUTE(AGGREGATE NONE, GATHER) */ dept, COUNT(*) FROM t1 GROUP BY dept;

注意:说明:

注意:1. HASH 和 BROADCAST 作为 dist_type 值目前暂不支持,使用时会被视为未指定(等同于不写该 Hint)。

注意:2. 当仅指定一个 dist_type 用于表目标时,它控制本表的分布方式,JOIN 对端不受约束。

注意:3. 当指定两个 dist_type 用于表目标时,第一个参数控制对端,第二个参数控制本表。

注意:4. Hint 仅在并行查询(Parallel Query)开启时生效。

注意:5. 多个 PQ_DISTRIBUTE Hint 可在同一条 SQL 中组合使用,分别控制不同目标。

十四、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=TREESELECT /*+ NO_MERGE(t) PQ_DISTRIBUTE(t1 NONE) */ t1.aFROM t1, (PQ_INLINE_EVALUATION FROM t2) tWHERE t1.a = t.a;

十五、SUBQUERY(MATERIALIZATION) 与 SUBQUERY(INTOEXISTS)

SUBQUERY Hint 除了上述并行策略外,还支持 MySQL 标准的 MATERIALIZATION 和 INTOEXISTS 策略,用于控制关联子查询的执行路径:

·MATERIALIZATION:强制子查询使用物化执行路径(将子查询结果物化为临时表,外表通过 Hash 查找匹配)。该策略同时会触发物化解关联优化,将关联 EXISTS/IN 改写为非关联 IN 后物化执行。

·INTOEXISTS:强制跳过物化解关联优化,回退为 EXISTS 逐行执行路径。

注意:说明:

注意:MATERIALIZATION 与 INTOEXISTS 互斥,同一子查询只能指定其中一个。MATERIALIZATION 还可与并行策略 PQ_PRE_EVALUATION / PQ_INLINE_EVALUATION 组合使用(逗号分隔)。

示例:

— 强制物化执行(适用于 OR 条件中的 EXISTS 等 Semi-Join 无法处理的场景)SELECT * FROM t1WHERE t1.a = 1 OR EXISTS (SELECT /*+ SUBQUERY(MATERIALIZATION) */ 1 FROM t2 WHERE t2.b = t1.b AND t2.c > 10);– 强制 EXISTS 路径(禁用物化解关联)SELECT * FROM t1WHERE t1.a = 1 OR EXISTS (SELECT /*+ SUBQUERY(INTOEXISTS) */ 1 FROM t2 WHERE t2.b = t1.b AND t2.c > 10);– MATERIALIZATION + 并行策略组合SELECT * FROM t1WHERE t1.a IN ( SELECT /*+ SUBQUERY(MATERIALIZATION, PQ_PRE_EVALUATION) */ a FROM t2);

十六、INLIST_TO_JOIN / NO_INLIST_TO_JOIN Hint

INLIST_TO_JOIN 和 NO_INLIST_TO_JOIN Hint 用于控制长 IN 列表转 Semi-Join 优化(inlist_to_join)的启用与禁用。

·INLIST_TO_JOIN(@qb_name):强制对指定 Query Block 启用长 IN 列表转 Semi-Join 变换,可绕过软约束(如阈值检查、LHS 索引要求、NOT IN 限制、低基数时间类型列限制等)。但硬约束(如 BLOB/JSON/GEOMETRY 类型、OR 上下文、外连接 ON 等)不可绕过。

·NO_INLIST_TO_JOIN(@qb_name):强制禁用指定 Query Block 的长 IN 列表转 Semi-Join 变换。支持跨 QB 靶向(如 @subq1 指定子查询块)。

注意:说明:

注意:当 IN 列表元素数量未达到 qt_inlist_to_join_threshold(默认 5000)阈值时,可通过 INLIST_TO_JOIN Hint 强制启用变换。变换后使用 EXPLAIN FORMAT=TREE 可看到 Materialize + scan on in-list: N rows 节点。

示例:

— 强制启用(即使元素数低于阈值)EXPLAIN FORMAT=TREESELECT /*+ INLIST_TO_JOIN(@`select#1`) */ * FROM t WHERE a IN (1,2,3,5);– 强制禁用(即使元素数超过阈值)SELECT /*+ NO_INLIST_TO_JOIN(@`select#1`) */ * FROM tWHERE a IN (1,2,3,…,5001);

十七、SET_VAR 方式

通过 SET_VAR Hint 在单条 SQL 中临时修改变量值,无需全局或会话级 SET:

— 临时降低阈值,让本查询走并行SELECT /*+ SET_VAR(parallel_scan_records_threshold=1000) */ * FROM t1;– 临时开启 Parallel Hash JoinSELECT /*+ SET_VAR(tdsql_enable_parallel_hash_join=ON) */ * FROM t1, t2 WHERE t1.b = t2.b;

十八、JOIN_RUNTIME_FILTER/NO_JOIN_RUNTIME_FILTER Hint 运行时过滤控制

JOIN_RUNTIME_FILTER 与 NO_JOIN_RUNTIME_FILTER Hint 用于控制 Hash Join 场景下的运行时过滤器(Runtime Filter)功能。执行 Hash Join 时,优化器可以基于构建表(Build 表)连接列的取值生成一个 Bloom Filter,并下推到探测表(Probe 表)的扫描路径上,在扫描阶段提前过滤掉不满足连接条件的行,从而减少参与 Join 的数据量。JOIN_RUNTIME_FILTER 用于强制启用该功能,NO_JOIN_RUNTIME_FILTER 用于禁用该功能。

十九、语法

/*+ JOIN_RUNTIME_FILTER([tbl_name[, tbl_name…]] [bloom]) *//*+ NO_JOIN_RUNTIME_FILTER([tbl_name[, tbl_name…]] [bloom]) */

参数 说明
tbl_name 可选。指定生效的探测表,可同时指定多个表,用逗号分隔。该 Hint 只对探测表生效,若指定到构建表则设置不生效。不指定表名时,作用于当前查询块内所有探测表。
bloom 可选。显式声明过滤器类型。当前仅支持 Bloom Filter 一种类型,可省略不写。

20、生效范围与优先级

JOIN_RUNTIME_FILTER/NO_JOIN_RUNTIME_FILTER 既可以作为查询块级设置(不带表名),也可以作为表级设置(带表名),二者可以在同一条语句中混合使用,表级设置的优先级高于查询块级设置。

注意:说明:

注意:例如 /*+ NO_JOIN_RUNTIME_FILTER() JOIN_RUNTIME_FILTER(t2) */ 表示当前查询块内除 t2 外的其他探测表均禁用运行时过滤,t2 因为有单独的表级设置而仍然强制启用。

21、前提条件

使用 Hint 前,需确认以下条件均已满足,否则 Hint 不会生效:

·会话或全局 optimizer_switch 中的 join_runtime_filter 开关需为 on(默认为 off)。Hint 只能在该开关打开的前提下进一步启用或禁用,开关关闭时 Hint 不生效。

·系统变量 runtime_bloom_filter_max_size(单位 MB,默认值 64,取值范围 0 – 128)不能为 0,否则运行时过滤功能整体不可用。

·探测表必须是 TDStore 存储引擎的普通表,不支持临时表。

·连接类型必须是内连接(Inner Join)且包含等值连接条件;不支持外连接(LEFT JOIN/RIGHT JOIN),也不支持由 NOT EXISTS、NOT IN 等改写为反连接的场景。

·连接条件中不能包含子查询。

·当前语句需为快照读,不支持加锁读(如 SELECT … FOR UPDATE)以及执行 LOCK TABLES 期间的查询。

22、与代价评估的关系

未使用 Hint 时,优化器会根据探测表的预估行数和构建表的预估基数(NDV)综合判断是否值得使用运行时过滤,仅在预估收益足够时才自动启用,探测表行数过少或预估过滤比例过低时会放弃使用。JOIN_RUNTIME_FILTER 会跳过该代价评估,只要满足前提条件即强制启用;NO_JOIN_RUNTIME_FILTER 则直接禁用,不再参与代价评估。

23、查看执行效果

可以通过 EXPLAIN 查看运行时过滤是否生效:

·传统格式 EXPLAIN:探测表所在行的 Extra 列会出现 Using join runtime filter (…)。

·EXPLAIN FORMAT=TREE:对应 Hash Join 节点后追加 with join runtime filter: bloom(…)。

·EXPLAIN FORMAT=JSON:探测表节点包含 using_join_runtime_filter 字段。

24、示例

— 查询块级:对当前查询块内所有探测表禁用运行时过滤SELECT /*+ NO_JOIN_RUNTIME_FILTER() */ * FROM t1 JOIN t2 ON t1.a = t2.a;– 表级:仅对探测表 t2 强制启用运行时过滤SELECT /*+ JOIN_RUNTIME_FILTER(t2) */ * FROM t1 JOIN t2 ON t1.a = t2.a;– 表级:仅禁用探测表 t2 的运行时过滤,t3 不受影响SELECT /*+ NO_JOIN_RUNTIME_FILTER(t2) */ STRAIGHT_JOIN * FROM t1 JOIN t2 ON t1.a = t2.a JOIN t3 ON t2.a = t3.a;– 混合使用:查询块级禁用,表级对 t2 单独强制启用SELECT /*+ NO_JOIN_RUNTIME_FILTER() JOIN_RUNTIME_FILTER(t2) */ STRAIGHT_JOIN * FROM t1 JOIN t2 ON t1.a = t2.a JOIN t3 ON t2.a = t3.a;

赞 (0)
上一篇 4天前
下一篇 4天前