
StarRocks Query Hint 全解析SET_VAR、SET_USER_VARIABLE 与 Join Hint 的语法、原理与最佳实践【免费下载链接】starrocksThe worlds fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocksQuery Hint查询提示是 StarRocks 提供给用户的一种显式干预查询优化器决策的机制通过形如/* ... */的注释指令让用户在单个查询内直接指定系统变量取值或 Join 执行方式。本文以 query_hint.md 为核心骨架结合 HintFactory.java 等 FE 端源码系统讲解三类 HintSET_VAR、SET_USER_VARIABLE、Join Hint的语法、适用场景、底层解析逻辑与实战用法帮助读者在 StarRocks 中精确掌控语句级优化行为在不影响整个会话的前提下解决特定查询的优化问题。Hint 概览单语句级别的优化指令Query Hint 是显式提供给查询优化器的指令或注释用于建议优化器以特定方式执行查询。与在会话级别全局生效的系统变量或用户自定义变量不同Hint 只在其所在的单条语句内生效不会影响整个会话的后续行为因此非常适合对个别病态查询做定点优化。目前 StarRocks 支持三类 HintHint 类型作用对象典型用途SET_VAR系统变量语句级临时修改query_timeout、streaming_preaggregation_mode等SET_USER_VARIABLE用户自定义变量语句级绑定变量v3.2.4 起支持可在 SELECT/INSERT 中复用标量子查询结果Join HintJoin 执行方式显式建议 Shuffle / Broadcast / Bucket Shuffle / Colocate Join或禁用 Join Reorder从 FE 端实现看Hint 的解析入口集中在 HintFactory.java语法解析器从 SQL 文本中取出/* ... */块后按前缀依次匹配SET_VAR与SET_USER_VARIABLE对于既不是SET_VAR也不是SET_USER_VARIABLE的 Hint 格式会被当作普通注释忽略return null不会报错。这也意味着拼写错误的 Hint 不会导致查询失败但也不会产生任何优化效果。SET_VAR语句级系统变量 HintSET_VARHint 允许在 SELECT 与 SUBMIT TASK 语句中设置一个或多个系统变量随后再执行该语句也可以用在其他语句内嵌的 SELECT 子句中例如CREATE MATERIALIZED VIEW AS SELECT与CREATE VIEW AS SELECT。需要特别注意的是如果SET_VARHint 被写在 CTE 的 SELECT 子句中即使语句执行成功该 Hint 也不会生效——Hint 必须作用在真正驱动查询的外层 SELECT 上。与系统变量的常规用法会话级别生效相比SET_VAR是语句级别生效它只改变当前语句的执行参数会话中的变量取值保持不变。这种一次一语句的语义让 Hint 成为执行参数微调最安全的手段。语法[...] SELECT /* SET_VAR(keyvalue [, key value]) */ ... SUBMIT [/* SET_VAR(keyvalue [, key value]) */] TASK ...要点key是系统变量名多个赋值用逗号分隔支持key value与keyvalue两种写法SET_VAR关键字本身不区分大小写变量名解析时会被统一转为小写见 HintFactory.java赋值内容支持带引号的字符串字面量如force_streaming。示例聚合模式下面的查询通过SET_VAR同时设置streaming_preaggregation_mode与new_planner_agg_stage两个系统变量指定聚合查询的执行模式SELECT /* SET_VAR (streaming_preaggregation_mode force_streaming,new_planner_agg_stage 2) */ SUM(sales_amount) AS total_sales_amount FROM sales_orders;streaming_preaggregation_mode控制流式预聚合模式force_streaming强制走流式聚合路径new_planner_agg_stage控制聚合分阶段执行方式取值2表示两阶段聚合本地预聚合 全局合并常用于大表聚合时减少网络传输。示例SUBMIT TASK 超时SET_VAR也可以直接写在SUBMIT TASK语句中为异步任务设置执行超时。下面的语句将insert_timeout设为 3 秒SUBMIT /* SET_VAR(insert_timeout3) */ TASK AS CREATE TABLE temp AS SELECT count(*) AS cnt FROM tbl1;示例物化视图构建超时创建异步刷新的物化视图时构建/刷新查询可能非常耗时可通过内嵌 SELECT 子句中的SET_VAR放大query_timeoutCREATE MATERIALIZED VIEW mv PARTITION BY dt DISTRIBUTED BY HASH(key) BUCKETS 10 REFRESH ASYNC AS SELECT /* SET_VAR(query_timeout500) */ * from dual;示例嵌套查询中的 HintHint 应写在主查询的 SELECT 上。即使查询体来自 CTE也只有在最外层 SELECT 使用SET_VAR才会生效-- To specify hints in the main query WITH t AS (SELECT region, sales_amount FROM sales_orders) SELECT /* SET_VAR (streaming_preaggregation_mode force_streaming, new_planner_agg_stage 2) */ SUM(sales_amount) AS total_sales_amount FROM t;底层解析原理从源码看SET_VARHint 的解析在 HintFactory.java 的buildSetVarHint中完成解析器按与,将keyvalue切分成键值对序列要求键值对数量必须为偶数否则返回nullHint 被忽略并且支持带引号与转义的字符串值。最终生成的SetVarHint以key.toLowerCase()的形式存储变量名说明变量名匹配对大小写不敏感。解析失败时如出现不成对的括号、多余的等号会抛出ParsingException并携带原始 Hint 文本提示非法取值。SET_USER_VARIABLE语句级用户自定义变量 HintSET_USER_VARIABLEHint 允许在 SELECT 或 INSERT 语句中设置一个或多个用户自定义变量。若其他语句包含 SELECT 子句也可在该 SELECT 子句中使用该 Hint。注意CREATE MATERIALIZED VIEW AS SELECT与CREATE VIEW AS SELECT语句中不允许使用该 Hint与SET_VAR类似写在 CTE 的 SELECT 子句中也不会生效。该能力自StarRocks v3.2.4起支持。与用户自定义变量的常规用法会话级别生效相比SET_USER_VARIABLE同样是语句级生效不影响整个会话。语法[...] SELECT /* SET_USER_VARIABLE(var_name expr [, var_name expr]) */ ... INSERT /* SET_USER_VARIABLE(var_name expr [, var_name expr]) */ ...要点变量名以开头赋值表达式可以是标量子查询或普通表达式括号内支持多个var_name expr赋值用逗号分隔。示例复用标量子查询结果以下 SELECT 引用了select max(age) from users与select min(name) from users两个标量子查询。直接书写会导致子查询被重复执行使用SET_USER_VARIABLE把它们绑定为变量a、b然后在 WHERE 条件中引用可提升可读性并避免重复计算SELECT /* SET_USER_VARIABLE (a (select max(age) from users), b (select min(name) from users)) */ * FROM sales_orders WHERE sales_orders.age a and sales_orders.name b;底层解析原理SET_USER_VARIABLE在 HintFactory.java 的buildUserVariableHint中解析Hint 内容必须整体以(开头并以)结尾内部被解析为逗号分隔的表达式列表每个表达式必须是一个左操作数为var_name的等值比较BinaryType.EQ否则整个 Hint 解析失败返回null。源码注释明确指出这里使用ImmutableMap.Builder保证用户自定义 Hint 变量之间按书写顺序的依赖关系——即后定义的变量可以引用前面已经定义的变量。解析结果封装为 UserVariableHint其中常量LEAST_LEN 19用于最小长度校验。Join Hint显式指定 Join 执行方式对于多表 Join 查询优化器通常会基于统计信息选择最优的 Join 执行方式如通过 ReorderJoinRule 进行 Join Reorder。在特殊场景下如统计信息不准、数据倾斜、需要与数据分布特性对齐时可以使用 Join Hint 显式向优化器建议 Join 执行方式或禁用 Join Reorder。Join Hint 目前支持四种 Join 执行方式Shuffle Join、Broadcast Join、Bucket Shuffle Join、Colocate Join。使用 Join Hint 时必须理解其两个重要行为一旦使用 Join Hint优化器不再执行 Join Reorder。因此用户需要自行保证将较小的表放在右侧right table——StarRocks 的 Hash Join 以右表作为哈希表构建侧选择小表做右表可以显著降低构建开销当建议Colocate Join 或 Bucket Shuffle Join时参与 Join 的表的数据分布必须满足对应 Join 方式的要求如属于同一 Colocation Group、分桶键匹配等否则建议的 Join 方式无法生效优化器会退回其他可行的执行方式。从源码看Join Hint 在 LogicalJoinOperator 中以joinHint字段保存且在构造时统一StringUtils.upperCase(joinHint)转为大写因此Join Hint 不区分大小写。ReorderJoinRule.java 中的extractRootInnerJoin在识别可重排的 Join 树时首先检查getJoinHint().isEmpty()一旦发现存在 Join Hint 就立即停止提取并跳过重排——这正是使用 Join Hint 后不再 Join Reorder的代码级依据。语法... JOIN { [BROADCAST] | [SHUFFLE] | [BUCKET] | [COLOCATE] | [UNREORDER]} ...其中UNREORDER表示仅禁用 Join Reorder 而不指定具体 Join 方式让优化器在原始表连接顺序下自行选择执行方式。:::note Join Hint 大小写不敏感。 :::Shuffle Join当需要将表 A、B 中具有相同分桶键bucketing key值的数据行 Shuffle 到同一台机器上再执行 Join 时可建议 Shuffle Joinselect k1 from t1 join [SHUFFLE] t2 on t1.k1 t2.k2 group by t2.k2;适用场景两表连接键与分布无关、无法走本地 Join且两侧数据量都较大、需要借助网络 Shuffle 来对齐数据时。Broadcast Join当表 A 是大表、表 B 是小表时可建议 Broadcast Join表 B 的数据被完整广播到表 A 数据所在的所有机器然后执行 Join。相比 Shuffle JoinBroadcast Join 省去了对表 A 数据进行 Shuffle 的开销但代价是每个节点都需要持有完整的右表副本select k1 from t1 join [BROADCAST] t2 on t1.k1 t2.k2 group by t2.k2;Bucket Shuffle Join当 Join 等值连接表达式包含表 A 的分桶键尤其是 A、B 均为大表时可建议 Bucket Shuffle Join表 B 的数据按照表 A 的数据分布被 Shuffle 到表 A 数据所在的机器上然后执行 Join。相比 Broadcast Join表 B 的数据全局只被 Shuffle 一次因此显著减少了数据传输量select k1 from t1 join [BUCKET] t2 on t1.k1 t2.k2 group by t2.k2;注意参与 Bucket Shuffle Join 的表必须是非分区表或共置colocated的表。Colocate Join当表 A、B 属于创建表时指定的同一个 Colocation Group 时相同分桶键值的数据行会分布在同一个 BE 节点上。若 Join 等值连接表达式同时包含两表的分桶键可建议 Colocate Join使相同键值的数据直接在本节点内完成 Join避免跨节点数据传输从而大幅降低传输耗时、提升查询性能select k1 from t1 join [COLOCATE] t2 on t1.k1 t2.k2 group by t2.k2;关于 Colocation Group 的建表方式与共置要求可进一步阅读 Colocate Join 文档。查看 Join 实际执行方式使用EXPLAIN命令可以查看查询实际的 Join 执行方式。若返回的执行计划中 Join 方式与 Hint 一致说明 Hint 已生效EXPLAIN select k1 from t1 join [COLOCATE] t2 on t1.k1 t2.k2 group by t2.k2;如上图所示EXPLAIN输出中join op: INNER JOIN (COLOCATE)明确标注了 Join 执行方式为 Colocate Join并带有colocate: true位置关联标志equal join conjunct: Z.k2 t.k2给出了等值连接条件。验证 Hint 是否生效时重点核对这一行的括号标注即可。Join Hint 使用决策建议数据分布已对齐优先本地 Join若两表属于同一 Colocation Group优先考虑[COLOCATE]若连接键包含左表分桶键且右表可一次 Shuffle 对齐考虑[BUCKET]小表优先广播右表数据量明显小于左表时[BROADCAST]可省去左表 Shuffle 成本但要注意右表副本放大问题大表间对齐两侧都是大表且无法本地 Join 时[SHUFFLE]是兜底选择但要警惕 Shuffle 带来的网络开销放弃重排但不干预方式只想固定 Join 顺序、让优化器在给定顺序下自选执行方式时使用[UNREORDER]牢记小表放右侧由于 Hint 会禁用 Join Reorder务必自行将较小表写在JOIN右侧否则可能构建出过大的哈希表得不偿失。三类 Hint 对比与选型小结维度SET_VARSET_USER_VARIABLEJoin Hint生效级别单条语句单条语句单条语句适用语句SELECT、SUBMIT TASK 及内嵌 SELECTCREATE MV/VIEW 可SELECT、INSERT 及内嵌 SELECTCREATE MV/VIEW 不可多表 Join 查询核心作用语句级调整系统变量语句级复用标量子查询/表达式指定 Join 方式或禁用 Reorder版本要求长期支持v3.2.4 起长期支持CTE 内 SELECT 使用不生效不生效—大小写关键字不敏感关键字不敏感不敏感实际调优中三者可以组合使用先用EXPLAIN观察计划瓶颈再用 Join Hint 修正 Join 方式、用SET_VAR调整聚合/超时等执行参数最后用EXPLAIN二次验证 Hint 是否真正生效。Hint 的核心价值在于把优化决策的控制权交还给用户——当统计信息失真或数据分布特殊导致优化器选型不佳时它是无需改动表结构即可完成定点优化的轻量级手段。【免费下载链接】starrocksThe worlds fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考