StarRocks query_dump 接口:完整抓取 SQL 执行上下文用于问题排查
【免费下载链接】starrocksThe world's 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
当你在 StarRocks 中执行 SQL 遇到Unknown Error、异常报错,或执行计划不符合预期(分区裁剪未生效、Join 顺序不合理)时,仅凭一条报错信息往往无法定位根因。StarRocks 在 FE 的 HTTP 服务上提供了query_dump接口:它会在 FE 侧重新走一遍查询的解析、分析与优化流程,把规划器所依赖的 SQL 语句、建表语句、会话变量、统计信息、执行计划成本信息和异常堆栈一次性打包为 JSON 返回。读完本文,你将掌握该接口的调用语法与参数含义、脱敏(desensitize)机制的原理,并能结合 FE 源码理解 dump 数据是如何在查询链路中被采集、序列化的,以及如何利用 dump 文件在离线测试环境中回放复现问题。
适用场景
根据官方 FAQ 文档 Dump_query.md 的说明,遇到以下情况时适合使用query_dump抓取上下文并提交给 StarRocks 技术支持排查:
- 执行 SQL 查询或
EXPLAIN时返回Unknown Error; - 执行 SQL 查询时返回错误信息或异常;
- 查询执行效率不符合预期,或执行计划存在可优化空间(例如分区裁剪没有生效、Join 顺序可以调整)。
其核心价值在于:技术支持拿到的不只是一句报错,而是 FE 规划该查询时所看到的全部输入——表元数据、行数、列统计、会话变量乃至 BE 的硬件规格——从而可以在离线环境中精确复现同一份执行计划。
query_dump 返回的信息
query_dump接口返回 FE 执行 SQL 时依赖的信息,包括:
| 字段 | 含义 | 源码佐证 |
|---|---|---|
statement | 原始查询语句 | QueryDumpSerializer.java#L114 写入 |
table_meta | 涉及的每张表的完整CREATE TABLE语句(密码等敏感配置会被隐藏) | 同上,L126-L131 通过AstToStringBuilder.getDdlStmt生成 |
table_row_count | 表/分区的行数(用于规划器估算基数) | QueryDumpSerializer.java#L199-L207 |
column_statistics | 列统计信息(Min/Max、NDV 等,带ESTIMATE标记) | 同上,L222-L232 |
explain_info | 带成本的EXPLAIN文本(TExplainLevel.COSTS) | StmtExecutor.java#L896 在生成执行计划后写入 |
session_variables | 会话变量全量 JSON 快照 | QueryDumpSerializer.java#L71-L75 |
be_number/be_core_stat | 存活 BE 数量、每个 BE 的硬件核数(如numOfHardwareCoresPerBe、cachedAvgNumOfHardwareCores) | QueryDumpSerializer.java#L77-L82 |
exception | 异常信息(异常堆栈),无异常时为空数组 | StmtExecutor.java#L1622-L1625 |
version/commit_version | 集群版本号与 commit hash(单元测试环境不输出) | QueryDumpSerializer.java#L89-L93 |
从源码结构看,当前实现还会按需追加若干面向外部目录(Hive/Iceberg 等)与优化器特性的字段,例如:
external_table_catalog:外部目录表的真实 catalog 名称,使回放可以直接按名重建外部 catalog;external_table_row_count、external_table_partition_spec、external_table_partition_names:外部表行数与 Iceberg 分区 spec,用于复现分区裁剪;hms_table:Hive 元数据表信息;view_meta:涉及视图时附带的视图定义;global_dict、column_min_max:低基数全局字典与列 Min/Max 元数据,用于复现依赖 BE 侧元数据的改写(如 meta-scan 改写、低基数 Decode 优化);partition_values:自动分区(表达式 Range/List)表的代表性分区值,用于回放时重建分区。
这些字段均按“db.table”键组织,详见 QueryDumpSerializer.java#L133-L271。
元信息脱敏(desensitize)
为保护数据隐私,StarRocks 会对库名、表名、列名等元信息做脱敏处理,并用脱敏后的名称重写查询语句。脱敏默认开启,若脱敏过程发生异常则回退使用原始信息(同时在exception数组中记录该异常);如需绕过脱敏,可在 HTTP URI 中附加mock=false。
从源码看,脱敏判定逻辑位于 QueryDumpSerializer.java#L97-L99:只要 FE 配置enable_desensitize_query_dump为true(见 Config.java#L5174-L5175,可动态修改),或本次请求mock=true,即进入脱敏路径。脱敏重写的具体实现在 DesensitizedSQLBuilder.java 中:它先由DesensitizedInfoCollector建立“原始名称 → 模拟名称”的映射字典,再依次对 SQL 文本、建表语句、视图定义、explain 输出做替换。脱敏命名规则为:
- 库名 →
db_mock_NNN(如tpch→db_mock_000); - 表名 →
tbl_mock_NNN(如lineitem→tbl_mock_001); - 列名/别名 →
mock_NNN(如L_RETURNFLAG→mock_012)。
值得注意的两点细节:
- 脱敏路径下,统计信息中可能携带真实数据样本的字段会被剥离——
stripSensitiveStatisticValues会去掉直方图(histogram)与 min/max 字符串值(QueryDumpSerializer.java#L474-L480),因为这类值本身就是原始列数据; explain_info通过整词正则替换脱敏(QueryDumpSerializer.java#L507-L532),只要有一处无法映射,就放弃输出 explain,避免泄漏未脱敏的名称。
接口语法与参数
query_dump是一个 HTTP POST 接口,注册路径为/api/query_dump(见 QueryDumpAction.java#L42-L55)。基本语法:
fe_host:fe_http_port/api/query_dump?db=${database}&mock=${value} post_data=${Query}使用wget的典型调用方式(查询语句放在文件中,通过--post-file作为 POST body 提交):
wget --user=${username} --password=${password} --post-file ${query_file} "http://${fe_host}:${fe_http_port}/api/query_dump?db=${database}&mock=${value}" -O ${dump_file}参数说明:
| 参数 | 说明 |
|---|---|
query_file | 包含待 dump 查询语句的文件,内容作为 POST body 发送 |
dump_file | 输出文件,保存接口返回的 JSON |
db | SQL 执行所在的数据库。若查询中已包含use db,该参数可省略;否则必须指定 |
mock | 是否启用元信息脱敏,默认true |
两个源码级补充:
db参数支持catalog.database形式。QueryDumpAction.java#L66-L76 会按.拆分参数值,若为两段则第一段作为 catalog、最后一段作为库名。这意味着对外部目录(如 Iceberg/Hive catalog)中的表做 dump 时,可以写db=iceberg_catalog.mydb来明确上下文;- 鉴权要求。当集群启用了 HTTP 认证时,该接口会校验操作权限(
requireOperateIfHttpAuthEnabled,见 QueryDumpAction.java#L57-L59),因此调用账户需要相应权限。文档示例中的--user=root --password=123即对应此要求。
使用示例
关闭脱敏(mock=false)
假设待分析的 TPC-H Q1 语句保存在query_file中:
select l_returnflag, l_linestatus, sum(l_quantity) as sum_qty, ... from lineitem where l_shipdate <= date '1998-12-01' group by l_returnflag, l_linestatus order by l_returnflag, l_linestatus;执行命令:
wget --user=root --password=123 --post-file query_file "http://127.0.0.1:8030/api/query_dump?db=tpch&mock=false" -O dump_file返回数据为 JSON 格式。以下示例保留了关键字段,session_variables与explain_info做了截断示意(完整输出中它们是整段 JSON 字符串 / 多行执行计划文本):
{ "statement": "select\n l_returnflag,\n l_linestatus,\n sum(l_quantity) as sum_qty, ...", "table_meta": { "tpch.lineitem": "CREATE TABLE `lineitem` (\n `L_ORDERKEY` int(11) NOT NULL,\n `L_QUANTITY` double NOT NULL,\n ... \n) ENGINE=OLAP \nDUPLICATE KEY(`L_ORDERKEY`)\nCOMMENT \"OLAP\"\nDISTRIBUTED BY HASH(`L_ORDERKEY`) BUCKETS 20 \nPROPERTIES (\n\"replication_num\" = \"1\",\n\"in_memory\" = \"false\",\n\"enable_persistent_index\" = \"true\",\n\"replicated_storage\" = \"true\",\n\"compression\" = \"LZ4\"\n);" }, "table_row_count": { "tpch.lineitem": { "lineitem": 3 } }, "column_statistics": { "tpch.lineitem": { "L_TAX": "[1.0, 1.0, 0.0, 8.0, 1.0] ESTIMATE", "L_SHIPDATE": "[1.6094304E9, 1.6094304E9, 0.0, 4.0, 1.0] ESTIMATE", "L_RETURNFLAG": "[-Infinity, Infinity, 0.0, 1.0, 1.0] ESTIMATE" } }, "explain_info": "PLAN FRAGMENT 0(F02) ... 0:OlapScanNode table: lineitem, rollup: lineitem preAggregation: on Predicates: [11: L_SHIPDATE, DATE, false] <= '1998-12-01' partitionsRatio=1/1, tabletsRatio=20/20 actualRows=3, avgRowSize=54.0 ...", "session_variables": "{\"query_timeout\":300,\"enable_profile\":false,\"cbo_push_down_aggregate\":\"global\", ... }", "be_number": 1, "be_core_stat": { "numOfHardwareCoresPerBe": "{\"10004\":104}", "cachedAvgNumOfHardwareCores": 104 }, "exception": [], "version": "main_querydump", "commit_version": "0c4d8c8d3e" }各字段解读:
statement:FE 收到的原始 SQL,排查“用户到底执行了什么”时直接对照;table_meta:规划器视角的完整建表语句,含键类型、分桶、属性(如enable_persistent_index、replicated_storage),可核对表设计是否与预期一致;table_row_count:FE 侧记录的行数(示例中lineitem仅 3 行),是规划器基数估算的基础——若行数统计严重失准,往往直接导致选错 Join 顺序;column_statistics:各列统计区间与 NDV,ESTIMATE表示来自统计信息而非精确值;explain_info:TExplainLevel.COSTS级别的执行计划,包含每个 Fragment 的算子、cardinality 与列统计传播过程,可直接用于分析分区裁剪(partitionsRatio、tabletsRatio)与聚合下推行为;session_variables:完整会话变量快照,用于核对诸如query_timeout、cbo_*优化器开关等对计划有影响的配置;be_number与be_core_stat:BE 数量与硬件核数规格,帮助判断资源维度的问题;exception:若执行过程中捕获到异常,堆栈会记录在此,无异常则为空数组。
开启脱敏(默认)
wget --user=root --password=123 --post-file query_file "http://127.0.0.1:8030/api/query_dump?db=tpch" -O dump_file返回的 JSON 结构与上例一致,但所有元信息被替换为模拟名称,查询语句也被同步重写:
{ "statement": "SELECT tbl_mock_001.mock_012, tbl_mock_001.mock_007, sum(tbl_mock_001.mock_010) AS mock_019, ... FROM db_mock_000.tbl_mock_001 WHERE tbl_mock_001.mock_013 <= '1998-12-01' GROUP BY tbl_mock_001.mock_012, tbl_mock_001.mock_007 ORDER BY tbl_mock_001.mock_012 ASC, tbl_mock_001.mock_007 ASC", "table_meta": { "db_mock_000.tbl_mock_001": "CREATE TABLE db_mock_000.tbl_mock_001 (\nmock_008 int(11) NOT NULL,\nmock_010 double NOT NULL, ... \n) ENGINE=OLAP \nDUPLICATE KEY(mock_008)\nDISTRIBUTED BY HASH(mock_008) BUCKETS 20 \nPROPERTIES (\"replication_num\" = \"1\");" }, "table_row_count": { "db_mock_000.tbl_mock_001": { "tbl_mock_001": 3 } }, "column_statistics": { "db_mock_000.tbl_mock_001": { "mock_017": "[1.0, 1.0, 0.0, 8.0, 1.0] ESTIMATE", "mock_012": "[-Infinity, Infinity, 0.0, 1.0, 1.0] ESTIMATE" } }, "explain_info": "PLAN FRAGMENT 0(F02) ... 0:OlapScanNode table: mock_001, rollup: mock_001 ...", "session_variables": "{...}", "be_number": 1, "exception": [] }对比可见:tpch/lineitem/L_*全部被替换为db_mock_000/tbl_mock_001/mock_NNN,但表的形状(列类型、键、分桶数、行数、统计区间形态、计划结构)完整保留——这正是脱敏设计目标:既不泄漏业务元数据,又不丢失复现问题所需的全部结构信息。仓库中也提供了对应的脱敏 dump 样例文件,如 mock_example.json,可参考其完整结构。
源码实现走读:dump 数据从哪来
从源码结构看,一次query_dump请求的完整链路为:
- HTTP 入口:
QueryDumpAction注册 POST/api/query_dump,解析db、mock参数(mock缺省即为true),并将当前ConnectContext标记为 HTTP dump 请求(QueryDumpAction.java#L57-L85); - 重新执行查询规划:
QueryDumper.dumpQuery先校验库是否存在,然后用SqlParser解析语句并构造StmtExecutor执行——注意这里走的是与正常查询相同的分析/优化路径,只是不真正下发执行(QueryDumper.java#L54-L109); - 链路中持续填充 DumpInfo:
ConnectContext内嵌DumpInfo,当isHTTPQueryDump为真(或会话变量enable_query_dump开启,见 ConnectContext.java#L1087-L1095)时,分析器与元数据管理器在正常流程中“顺手”把元数据写入:语句原文(StmtExecutor.java#L846-L852)、表统计(如 MetadataMgr.java#L776-L814 中的列统计与外部表行数)、explain 文本(StmtExecutor.java#L896)、异常堆栈(StmtExecutor.java#L1622-L1625)。这也解释了为什么 dump 中的统计与计划信息“与规划器所见完全一致”; - 序列化返回:执行结束后取回
DumpInfo,经QueryDumpSerializer(Gson 定制序列化器)输出 JSON,mock参数决定走脱敏或原始路径(QueryDumper.java#L96-L103)。
一个值得注意的失败模式:如果语句没有走 CBO 规划器,dumpInfo为空,接口会返回BAD_REQUEST: not use cbo planner, try again.(QueryDumper.java#L101-L103)。此外,查询为空(query is empty)与库不存在(Database [...] does not exists,返回 404)也都有明确的状态码与错误信息。
dump 文件的离线回放价值
dump 文件不只是“给人看的报告”:QueryDumpInfo同时注册了反序列化器(QueryDumpDeserializer),FE 单元测试可以直接加载一个 dump JSON,在无任何真实集群的环境下重建元数据并复现同一条执行计划或同一个报错。仓库中已沉淀了大量此类回放用例:
- 回放框架与测试:ReplayFromDumpTest.java、ReplayFromDumpForSharedDataTest.java、ReplayWithMVFromDumpTest.java、QueryDumpHistogramReplayTest.java、QueryDumpExternalCatalogReplayTest.java;
- 覆盖典型问题的 dump 样例库:sql/query_dump/ 目录下有上百个真实问题的 dump 文件,例如 tpch01.json(TPC-H Q1 基线)、
prune_table_npe.json(表裁剪 NPE)、join_reorder.json(Join 重排)、auto_partition_month.json(自动分区)、hive_catalog_partition_skew.json(Hive 目录倾斜)等; - 脱敏样例:mock-files/mock_example.json。
这提示了一条实用工作流:遇到难以复现的规划器问题时,除了把 dump 文件提交给技术支持,还可以参考上述测试用例的写法,把 dump 文件固化为一个回放测试,使问题在 CI 中可长期回归。
注意事项小结
db参数在查询未显式use db时必传;跨 catalog 场景可用catalog.database形式指定;mock缺省即开启脱敏;绕过脱敏会输出真实库表列名与统计样本,仅应在受控环境(如技术支持协助排查、内部测试集群)下使用;- 脱敏失败不会导致请求失败,而是回退为原始内容并把异常写入
exception字段,使用脱敏结果前可先检查该字段; - 接口要求查询能走 CBO 规划器,否则返回
not use cbo planner错误; - 集群开启 HTTP 认证时,调用账户需具备相应权限(文档示例中的
--user/--password)。
通过上述机制,query_dump把“用户端一条模糊报错”转化为“FE 规划器完整输入快照”,是 StarRocks 问题排查中连接现场与离线复现之间的关键桥梁。
【免费下载链接】starrocksThe world's 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),仅供参考