SQL Plan Manager
Beta feature
本文介绍 SQL Plan Manager 功能的基本概念和使用场景,以及如何使用 SQL Plan Manager 规范化查询计划。
从 v3.5.0 开始,StarRocks 支持 SQL Plan Manager 功能。
概述
SQL Plan Manager 允许用户将查询计划绑定到查询上,从而防止查询计划因系统状态变化(主要是数据更新和统计信息更新)而改变,从而稳定查询性能。
工作流程
SQL Plan Manager 需要预先指定要绑定的查询 SQL 和要使用的查询计划(Baseline)。这里,查询指的是用户实际执行的查询 SQL,而查询计划是手动优化或添加提示的查询 SQL。
SQL Plan Manager 的工作流程如下:
- 创建 Baseline:使用
CREATE BASELINE命令将查询计划绑定到指定的查询 SQL。 - 查询改写:提交到 StarRocks 的查询会自动与 SQL Plan Manager 中存储的 Baseline 进行匹配。如果匹配成功,则使用 Baseline 的查询计划执行查询。
关于 Baseline 创建的注意事项:
- 必须确保 Baseline 中绑定的 SQL 与执行计划 SQL 之间的逻辑一致性。StarRocks 会进行基本的表和参数检查,但无法保证完全的逻辑一致性检查。确保执行计划的逻辑正确性是用户自己的责任。
- 默认情况下,Baseline 中绑定的 SQL 存储其自身的 SQL 指纹。默认情况下,SQL 中的常量值将被替换为变量参数(例如,将
t1.v1 > 1000改为t1.v1 > ?)以提高 SQL 匹配度。 - Baseline 中绑定的执行计划可以通过修改 SQL 逻辑或添加提示(Join Hints 或
Set_Var)进行定制,以确保生成所需的执行计划。 - 对于复杂的 SQL,StarRocks 可能无法自动将 SQL 和执行计划绑定到 Baseline。在这种情况下,可以使用手动绑定,详细信息请参见高级用法部分。
关于查询改写的注意事项:
- SQL Plan Manager 主要依赖于 SQL 指纹匹配。它检查查询的 SQL 指纹是否与某个 Baseline 匹配。如果查询匹配某个 Baseline,则查询中的参数会自动替换到 Baseline 的执行计划中。
- 在匹配过程中,如果查询能匹配多个状态为
enable的 Baseline,优化器会评估并选择最佳 Baseline。 - 在匹配过程中,SQL Plan Manager 会验证 Baseline 和查询是否匹配。如果匹配失败,则不会使用 Baseline 的查询计划。
- 对于 SQL Plan Manager 改写的执行计划,
EXPLAIN语句将返回Using baseline plan[id]。
管理 Baseline
创建 Baseline
语法:
CREATE [GLOBAL] BASELINE [ON <BindSQL>] USING <PlanSQL>
[PROPERTIES ("key" = "value"[, ...])]
参数:
GLOBAL:(可选)创建全局级别的 Baseline。BindSQL:(可选)要绑定到 Baseline(执行计划)查询的具体查询。如果未指定此参数,Baseline 查询将绑定到自身并使用其自己的查询计划。PlanSQL:用于定义执行计划的查询。
示例:
-- 创建会话级别的 BASELINE,直接将 Baseline SQL 绑定到自身并使用其自己的查询计划。
CREATE BASELINE
USING SELECT t1.v2, t2.v3 FROM t1 JOIN t2 ON t1.v2 = t2.v2 WHERE t1.v2 > 100;
-- 创建全局级别的 BASELINE,直接将 Baseline SQL 绑定到自身并使用其自己的查询计划,指定 Join Hints。
CREATE GLOBAL BASELINE
USING SELECT t1.v2, t2.v3 FROM t1 JOIN[BROADCAST] t2 ON t1.v2 = t2.v2 WHERE t1.v2 > 100;
-- 创建会话级别的 BASELINE,将查询绑定到 Baseline SQL,并使用指定 Join Hints 的 Baseline SQL 查询计划。
CREATE BASELINE ON SELECT t1.v2, t2.v3 FROM t1, t2 WHERE t1.v2 = t2.v2 AND t1.v2 > 100
USING SELECT t1.v2, t2.v3 FROM t1 JOIN[BROADCAST] t2 on t1.v2 = t2.v2 where t1.v2 > 100;
查看 Baseline
语法:
SHOW BASELINE [WHERE <condition>]
SHOW BASELINE [ON <query>]
示例:
MySQL > show baseline\G;
***************************[ 1. row ]***************************
Id | 646125
global | N
enable | N
bindSQLDigest | SELECT * FROM `td`.`t2` INNER JOIN `td`.`t1` ON `td`.`t2`.`v2` = `td`.`t1`.`v2` LIMIT 2
bindSQLHash | 1085294
bindSQL | SELECT * FROM `td`.`t2` INNER JOIN `td`.`t1` ON `td`.`t2`.`v2` = `td`.`t1`.`v2` LIMIT 2
planSQL | SELECT t2.v1 AS c_1, t2.v2 AS c_2, t2.v3 AS c_3, t1.v1 AS c_4, t1.v2 AS c_5 FROM t2 INNER JOIN[SHUFFLE] t1 ON t2.v2 = t1.v2 LIMIT 2
costs | 582.0
queryMs | -1.0
source | USER
updateTime | 2025-05-16 14:50:45
***************************[ 2. row ]***************************
Id | 636134
global | Y
enable | Y
bindSQLDigest | SELECT * FROM `td`.`t2` INNER JOIN `td`.`t1` ON `td`.`t2`.`v2` = `td`.`t1`.`v2` WHERE `td`.`t2`.`v3` = ?
bindSQLHash | 1085294
bindSQL | SELECT * FROM `td`.`t2` INNER JOIN `td`.`t1` ON `td`.`t2`.`v2` = `td`.`t1`.`v2` WHERE `td`.`t2`.`v3` = _spm_const_range(1, 10, 20)
planSQL | SELECT t_0.v1 AS c_1, t_0.v2 AS c_2, t_0.v3 AS c_3, t1.v1 AS c_4, t1.v2 AS c_5 FROM (SELECT * FROM t2 WHERE v3 = _spm_const_range(1, 10, 20)) t_0 INNER JOIN[SHUFFLE] t1 ON t_0.v2 = t1.v2
costs | 551.0204081632653
queryMs | -1.0
source | USER
updateTime | 2025-05-13 15:29:04
2 rows in set
Time: 0.019s
MySQL > show baseline where global = true\G;
***************************[ 1. row ]***************************
Id | 636134
global | Y
enable | Y
bindSQLDigest | SELECT * FROM `td`.`t2` INNER JOIN `td`.`t1` ON `td`.`t2`.`v2` = `td`.`t1`.`v2` WHERE `td`.`t2`.`v3` = ?
bindSQLHash | 1085294
bindSQL | SELECT * FROM `td`.`t2` INNER JOIN `td`.`t1` ON `td`.`t2`.`v2` = `td`.`t1`.`v2` WHERE `td`.`t2`.`v3` = _spm_const_range(1, 10, 20)
planSQL | SELECT t_0.v1 AS c_1, t_0.v2 AS c_2, t_0.v3 AS c_3, t1.v1 AS c_4, t1.v2 AS c_5 FROM (SELECT * FROM t2 WHERE v3 = _spm_const_range(1, 10, 20)) t_0 INNER JOIN[SHUFFLE] t1 ON t_0.v2 = t1.v2
costs | 551.0204081632653
queryMs | -1.0
source | USER
updateTime | 2025-05-13 15:29:04
1 row in set
Time: 0.013s
MySQL > show baseline on SELECT count(1) AS `count(1)` FROM `old`.`t1` INNER JOIN `old`.`t2` ON `old`.`t1`.`k2` = `old`.`t2`.`k2` LIMIT 10\G;
***************************[ 1. row ]***************************
Id | 679817
global | Y
enable | Y
bindSQLDigest | SELECT count(?) AS `count(1)` FROM `old`.`t1` INNER JOIN `old`.`t2` ON `old`.`t1`.`k2` = `old`.`t2`.`k2` LIMIT 10
bindSQLHash | 1085927
bindSQL | SELECT count(_spm_const_var(1)) AS `count(1)` FROM `old`.`t1` INNER JOIN `old`.`t2` ON `old`.`t1`.`k2` = `old`.`t2`.`k2` LIMIT 10
planSQL | SELECT count(_spm_const_var(1)) AS c_7 FROM (SELECT 1 AS c_9 FROM t1 INNER JOIN[SHUFFLE] t2 ON t1.k2 = t2.k2) t_0 LIMIT 10
costs | 2532.6
queryMs | 35.0
source | CAPTURE
updateTime | 2025-05-27 11:17:48
1 row in set
Time: 0.026s