StarRocks SQL 黑名单管理拦截危险 SQL 防止集群崩溃或高并发失控【免费下载链接】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/starrocksStarRocks 提供 SQL BlacklistSQL 黑名单机制用于在查询执行前拦截匹配特定正则模式的 SQL防止某些查询触发集群崩溃或引发意外的高并发负载。本文基于 StarRocks 官方文档 Blacklist Management 展开完整覆盖黑名单的启用、添加、查看与删除全流程并结合 FEFrontend源码剖析其在解析执行链中的拦截位置、正则匹配语义、元数据持久化方式以及运维中需要注意的边界条件帮助你在真实生产环境中安全地部署 SQL 拦截规则。适用场景与版本前提黑名单的定位很明确某些 SQL 模式一旦执行会导致 BE 崩溃、集群资源耗尽或产生不可控的高并发查询。管理员可以通过正则表达式把这些危险 SQL模式加入黑名单之后所有匹配这些模式的语句都会在 FE 侧被直接拒绝并给用户返回明确的错误提示。从当前仓库的源码结构看黑名单的拦截范围与文档描述一致适用于以下三类语句SELECT 语句黑名单自推出以来即支持INSERT 语句自 v3.1 起支持执行器入口 中对parsedStmt instanceof ... InsertStmt等类型做了判断CTASCREATE TABLE AS SELECT语句自 v3.4 起支持源码中对应CreateTableAsSelectStmt的分支判断。管理黑名单的操作要求当前用户具备ADMIN_PRIV权限。文档中的三条核心命令如下ADD SQLBLACKLIST sql; DELETE SQLBLACKLIST sql_index_number; SHOW SQLBLACKLIST;启用开关enable_sql_blacklist黑名单机制默认是关闭的需要通过enable_sql_blacklist配置项显式打开admin set frontend config (enable_sql_blacklist true);在 FE 源码中该配置项定义于 Config.javapublic static boolean enable_sql_blacklist false;默认值false与文档描述一致。这意味着即使你向黑名单中添加了规则只要开关未打开所有 SQL 都会照常执行黑名单不产生任何拦截效果admin set frontend config是动态配置命令修改后立即生效无需重启 FE该配置属于 frontend config作用域是整个 FE而非单个 session。拦截时机与匹配机制源码级解析理解黑名单在哪里生效和如何匹配有助于编写正确的正则规则。拦截逻辑位于 FE 查询执行入口 StmtExecutor.java核心条件为 Config.enable_sql_blacklist !parsedStmt.isExplain() !isProxy) { ... String originSql origStmt.originStmt.trim() .toLowerCase().replaceAll( , ); // If this sql is in blacklist, show message. GlobalStateMgr.getCurrentState().getSqlBlackList().verifying(originSql);从这段调用链可以确认几个关键行为拦截发生在执行阶段SQL 完成解析parse之后、正式执行之前进行校验因此即使 SQL 尚未运行也会报在黑名单中的错误EXPLAIN 不受拦截!parsedStmt.isExplain()表明执行EXPLAIN 被禁 SQL不会被黑名单拒绝便于排查某条 SQL 是否会被拦截统计连接被豁免源码中对context.isStatisticsConnection() || context.isStatisticsJob()的分支直接跳过黑名单检查。这与文档中禁止所有 INSERT INTO ... VALUES 但排除_statistics_.column_statistics的示例相呼应——FE 内部的统计信息收集走的是受信任通道默认不受黑名单影响代理请求Proxy不做此检查!isProxy条件说明经 Proxy 转发的查询不走黑名单分支。真正的匹配逻辑在 SqlBlackList.java 的verifying方法中public void verifying(String sql) throws AnalysisException { String formatSql sql.replace(\r, ).replace(\n, ).replaceAll(\\s, ); for (BlackListSql patternAndId : ruleSnapshot) { Matcher m patternAndId.pattern.matcher(formatSql); if (m.find()) { MetricRepo.COUNTER_SQL_BLOCK_HIT_COUNT.increase(1L); ErrorReport.reportSqlBlackListException(ErrorCode.ERR_SQL_IN_BLACKLIST_ERROR, patternAndId.id); } } }这段代码揭示了四个实现细节空白归一化待匹配的 SQL 会先把\r、\n替换为空格再把连续空白折叠为单个空格。因此正则规则不需要考虑 SQL 中换行、多空格等排版差异ADD SQLBLACKLIST添加规则时也会做同样的归一化见 addBlackSql使用Matcher.find()而非matches()即黑名单规则是部分匹配语义——只要 SQL 文本中任意位置出现与规则匹配的子串就会被拦截。这就是为什么文档中禁止count(*)的规则写成select count(\\*) from .这种从语句头部开始锚定的形式而不需要$结尾小写归一规则在添加时被统一转小写存储待匹配 SQL 也转小写。若需要匹配大小写敏感的写法如文档中的 INSERT 示例需借助正则自身的(?i)修饰符来控制而不是依赖规则字符串的大小写命中计数器每次命中都会增加COUNTER_SQL_BLOCK_HIT_COUNT指标你可以通过 FE 指标观察黑名单的实际拦截量验证规则是否按预期生效。命中黑名单后错误通过 ErrorReport.reportSqlBlackListException 上报客户端看到的错误形如ERROR 1064 (HY000): Access denied; sql select count (*) from test_all_type_select_2556 is in blacklist该错误类型由 SqlBlacklistedException继承自AnalysisException承载在 StmtExecutor 中被单独捕获处理保证用户拿到的是清晰的SQL 在黑名单中提示而不是普通的语法或权限错误。添加黑名单规则添加命令ADD SQLBLACKLIST sql;其中sql是描述某一类 SQL 的正则表达式。由于 SQL 本身大量使用(、)、*、.这些与正则语义冲突的字符需要区分处理(和)在 SQL 中出现过于频繁不需要转义其他特殊字符需要加反斜杠\前缀转义在 SQL 客户端里提交规则字符串时反斜杠本身通常还要再转义一次写作\\这与下面示例中的写法一致。完整继承官方文档的七类典型规则1. 禁止count(*)ADD SQLBLACKLIST select count(\\*) from .;2. 禁止count(distinct ...)ADD SQLBLACKLIST select count(distinct .) from .;3. 禁止特定范围的order by ... limit x, y1 ≤ x ≤ 75 ≤ y ≤ 7ADD SQLBLACKLIST select id_int from test_all_type_select1 order by id_int limit [1-7], [5-7];这里[1-7]、[5-7]是正则字符组表示单个数字字符因此能精确匹配limit 1,5、limit 3,7等组合。4. 禁止一条复杂的嵌套 EXCEPT 查询复杂 SQL 整体封禁ADD SQLBLACKLIST select id_int \\* 4, id_tinyint, id_varchar from test_all_type_nullable except select id_int, id_tinyint, id_varchar from test_basic except select (id_int \\* 9 \\- 8) \\/ 2, id_tinyint, id_varchar from test_all_type_nullable except select id_int, id_tinyint, id_varchar from test_basic_nullable;5. 禁止所有 INSERT INTO 语句ADD SQLBLACKLIST (?i)^insert\\sinto\\s.*;6. 禁止所有 INSERT INTO ... VALUES 语句ADD SQLBLACKLIST (?i)^insert\\sinto\\s.*values\\s*\\(;7. 禁止所有 INSERT INTO ... VALUES但排除对系统视图_statistics_.column_statistics的写入ADD SQLBLACKLIST (?i)^insert\\sinto\\s(?!column_statistics\\b).*values\\s*\\(;第 57 条示例用了(?i)忽略大小写与^锚点说明规则可以组合使用标准 Java 正则特性FE 底层即java.util.regex.Pattern。第 7 条的(?!column_statistics\\b)是负向前瞻用于全量禁止 例外放行的场景。添加规则时的源码行为补充见 SqlBlackList.addBlackSql规则字符串会先做trim、转小写、折叠空白再调用Pattern.compile编译正则本身不合法时会抛出SemanticException: Sql syntax error即规则错误在添加阶段就会被拒绝而不会污染黑名单重复添加同一条规则不会产生新条目put方法以规则的pattern.toString()为 key 存入ConcurrentHashMap若已存在则直接返回旧 id见 SqlBlackList.put。查看黑名单SHOW SQLBLACKLIST;结果格式为Index | Forbidden SQL其中Index即删除操作所需的规则编号。例如mysql show sqlblacklist; ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | Index | Forbidden SQL | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 1 | select count\(\*\) from . | | 2 | select id_int \* 4, id_tinyint, id_varchar from test_all_type_nullable except select id_int, id_tinyint, id_varchar from test_basic except select \(id_int \* 9 \- 8\) \/ 2, id_tinyint, id_varchar from test_all_type_nullable2 except select id_int, id_tinyint, id_varchar from test_basic_nullable | | 3 | select id_int from test_all_type_select1 order by id_int limit [1-7], [5-7] | | 4 | select count\(distinct .\) from . | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------注意Forbidden SQL列中展示的 SQL 已对所有 SQL 语义字符做了转义显示如\( \* \)这只是展示层的可读化处理内部实际参与匹配的是编译后的Pattern对象。从源码看SHOW SQLBLACKLIST返回的数据来自 SqlBlackList.getBlackLists其内容是一个按 id 排序的不可变快照ruleSnapshot。每次增删规则后都会执行refreshSnapshot()重建该列表读路径verifying遍历快照与写路径增删规则通过updateLock与 volatile 引用解耦——这意味着黑名单规则更新是原子的正在执行匹配的检查不会被中间状态影响。删除黑名单规则DELETE SQLBLACKLIST sql_index_number;sql_index_number是逗号分隔的规则编号列表。例如删除上一条SHOW结果中的第 3、4 号规则delete sqlblacklist 3, 4;删除后再次查看mysql show sqlblacklist; --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | Index | Forbidden SQL | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 1 | select count\(\*\) from . | | 2 | select id_int \* 4, id_tinyint, id_varchar from test_all_type_nullable except select id_int, id_tinyint, id_varchar from test_basic except select \(id_int \* 9 \- 8\) \/ 2, id_tinyint, id_varchar from test_all_type_nullable2 except select id_int, id_tinyint, id_varchar from test_basic_nullable | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------删除操作同样走元数据日志通道deleteBlackSql 会先写 EditLoglogDeleteSQLBlackList持久化载体为 DeleteSqlBlackLists再在 WAL 回调中从内存 map 移除对应 id 的规则并刷新快照。持久化规则可跨 FE 重启保留黑名单不是内存态的临时配置。从 SqlBlackList 的实现可以看到完整的持久化闭环添加/删除均通过 EditLog 写入元数据日志logAddSQLBlackList、logDeleteSQLBlackListFollower FE 通过回放日志保持与 Leader 的规则一致落盘镜像save方法把全部规则以 SqlBlackListPersistInfoid pattern 字符串的 JSON 形式写入 FE 镜像SRMetaBlockID.BLACKLIST_MGR块load方法在 FE 重启时从镜像恢复并重新编译Patternid 单调递增不回绕AtomicLong ids在加载镜像时会取持久化 id 的最大值续接保证SHOW SQLBLACKLIST中的编号在重启后依然稳定、可引用。因此一次ADD SQLBLACKLIST的效果是持久的FE 重启后规则仍在不需要重新配置要解除拦截只能显式DELETE SQLBLACKLIST或关闭enable_sql_blacklist总开关。相关机制与使用建议SQL Digest 黑名单是独立体系在 StmtExecutor 中当Config.enable_sql_digest或 session 变量enable_sql_digest开启时还会额外用SqlDigestBlackList源码对语句 digest 做匹配。它针对的是语句指纹而非原始文本适合拦截参数值不同但结构相同的一类 SQL与本文的文本正则黑名单互补规则设计建议由于匹配是find()部分匹配 小写归一化规则宜从语句头部select、^insert写起并尽量具体避免误伤正常查询上线新规则前可先用EXPLAIN验证语法、再观察COUNTER_SQL_BLOCK_HIT_COUNT指标确认命中情况统计作业豁免FE 的统计信息连接/作业_statistics_相关内部写入在黑名单检查前被显式跳过若你的规则要覆盖内部行为需注意这一豁免边界运维流程admin set frontend config修改的enable_sql_blacklist属于动态配置若希望该开关重启后保持建议同时评估将其固化到 FE 启动配置中以当前部署环境的配置管理方式为准。小结StarRocks 的 SQL 黑名单机制用动态总开关 正则规则集合 EditLog 持久化三层结构为管理员提供了一道轻量但持久化的 SQL 拦截防线用admin set frontend config (enable_sql_blacklist true)打开开关用ADD SQLBLACKLIST 正则封禁危险 SELECT / INSERT / CTAS 模式注意\\双重转义与(?i)、^、负向前瞻等正则特性用SHOW SQLBLACKLIST核对规则与 Index必要时DELETE SQLBLACKLIST id批量撤销规则命中会返回ERROR 1064 ... is in blacklist并可通过 FE 指标COUNTER_SQL_BLOCK_HIT_COUNT观测拦截量。相关源码入口黑名单核心实现、执行器拦截点、配置项定义、错误上报官方文档见 Blacklist Management。【免费下载链接】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),仅供参考