news 2026/9/24 11:40:10

Apache Druid SQL-based Ingestion(MSQ 引擎)已知问题全景指南:容错、语句限制与规避策略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Apache Druid SQL-based Ingestion(MSQ 引擎)已知问题全景指南:容错、语句限制与规避策略
  • 数据库
  • OLAP
  • 大数据
  • 后端

【免费下载链接】druid

Apache Druid: a high performance real-time analytics database.

项目地址:https://gitcode.com/gh_mirrors/druid6/druid
点击查看免费下载

导读

本文基于 Apache Druid 官方文档整理druid-multi-stage-query扩展(Druid 24.0 起引入)中多阶段查询(Multi-stage Query,简称 MSQ)任务引擎的完整已知问题清单,覆盖任务运行时行为、SELECT/INSERT/REPLACE/EXTERN/WINDOW语句的限制,并结合 extensions-core/multi-stage-query 模块源码解释每个限制的底层成因与对应的错误码、上下文参数,帮助你在规划 SQL 批量摄取方案时提前规避坑点,并在遇到报错时快速定位根因。

背景:MSQ 引擎与本文定位

SQL-based ingestion 由 Apache Druid 内置的druid-multi-stage-query扩展提供,它新增了一个可以运行 SQL 语句作为批处理任务的多阶段查询任务引擎。该引擎支持用INSERTREPLACE语句批量装载数据,并实验性地支持把SELECT查询作为批处理任务运行。关于如何选择批量摄取方法,可参考ingestion methods 表;关于加载扩展与权限要求(使用EXTERN需要 "EXTERNAL" 资源的 READ 权限),见 SQL-based ingestion 总览。

MSQ 引擎引入了三个核心执行角色(见 index.md 词汇表):

  • Controller:类型为query_controller的索引服务任务,每个查询有一个 controller 负责编排执行;
  • Worker:类型为query_worker的任务,实际执行查询,每个查询可有多个 worker,worker 内部利用处理线程池(至多druid.processing.numThreads)并行处理;
  • Stage / Partition / Shuffle:执行阶段被跨 worker 并行化;INSERT/REPLACE查询的最终阶段分区会直接成为 Druid 段(segment);worker 之间按分区交换数据(shuffle),每个输出分区按聚类键排序。

理解这些概念是消化下述已知问题的前提:很多限制的本质是「controller 单点职责」「worker 本地临时存储」「stage 间 shuffle 数据规模」三者的边界约束。

任务运行时的已知问题

容错只实现了一半:worker 可重启,controller 不可

MSQ 引擎的容错是部分实现的:

  • 当 worker 被意外杀死时,会被重新拉起(relaunch);
  • 而当controller 被意外杀死时,不会被重新拉起

从源码看,容错能力由上下文参数faultTolerance控制,其定义与默认值位于 MultiStageQueryContext.java(CTX_FAULT_TOLERANCE = "faultTolerance",默认false),读取逻辑在isFaultToleranceEnabled()(同文件 L200-L206)。同时 reference.md 明确指出:faultTolerance不能与显式设置为falsedurableShuffleStorage同时使用,启用 fault tolerance 前必须在服务器级别配置druid.msq.intermediate.storage.enable=true的持久化存储。

相关的重启次数限制也记录在 reference.md 的 Limits 表 中:

限制项超限错误码
单个 worker 的最大重启次数(首次运行不计,即最多运行 1 +workerRelaunchLimit次)2TooManyAttemptsForWorker
整个作业跨所有 worker 的最大重启次数100TooManyAttemptsForJob

实践建议:controller 是查询编排的单点。对于长时间运行的批量摄取,应尽量避免直接杀死 controller 任务;如需取消,请走/druid/indexer/v1/task/{taskId}/shutdownAPI(对应Canceled错误码,见 reference.md)。

worker 阶段输出占用本地磁盘,可能撑爆druid.indexer.task.baseDir

worker 任务的阶段输出存储在druid.indexer.task.baseDir指定的工作目录中。当某个 stage 产生大量输出数据时,可能耗尽该目录所在磁盘的全部空间,此时查询以UnknownError失败,错误信息包含"No space left on device"(对应错误码UnknownErrormessage字段,见 reference.md)。

这与另一个限制相关联:MSQ 引擎还有按 worker 的临时存储上限druid.indexer.task.tmpStorageBytesPerTask,若配置的临时存储不足以启动某个 stage,会返回NotEnoughTemporaryStorage错误(错误详情字段suggestedMinimumStorageconfiguredTemporaryStorage,见 reference.md)。

实践建议

  • 为 Indexer / MiddleManager 规划druid.indexer.task.baseDir所在磁盘容量时,把「中间 shuffle 数据 + 段生成临时文件」都算进去,不能只按最终段大小估算;
  • 遇到No space left on device时,检查各 worker 节点磁盘,清理历史任务遗留的临时文件;
  • 若报错为NotEnoughTemporaryStorage,按错误详情中的suggestedMinimumStorage调大druid.indexer.task.tmpStorageBytesPerTask

SELECT语句的已知问题

GROUPING SETS未实现

MSQ 引擎未实现GROUPING SETS。使用该特性的查询会返回QueryNotSupported错误。

错误码的源码定义位于 QueryNotSupportedFault.java:其CODE常量即"QueryNotSupported",属于BaseMSQFault的派生类型。在 reference.md 的错误码表 中对该错误的解释是:QueryKit无法把给定的原生查询翻译成多阶段查询,典型触发场景就是GROUPING SETS

规避思路:改用多个GROUP BY查询再UNION ALL组合结果,或改写为GROUP BY+ 常规聚合表达式。

INSERTREPLACE语句的已知问题

不支持带列列表的 INSERT / REPLACE

形如INSERT INTO tbl (a, b, c) SELECT ...带列列表写法未实现。这是与标准 SQL 的一个重要差异,规划查询时不要照搬传统数据库的写法。

列按「名称」而非「位置」匹配

INSERT ... SELECTREPLACE ... SELECT在插入列时依据列名匹配,这与 SQL 标准中「按位置插入列」的行为不同(标准行为见 reference.md 的 INSERT 章节)。

实践建议:在SELECT列表中显式使用AS为目标列赋名,不要依赖 SELECT 子句中的列顺序。例如:

INSERT INTO target_table SELECT src.event_time AS __time, src.user_id AS user_id, src.event_type AS event_type FROM TABLE(EXTERN(...)) PARTITIONED BY DAY

未覆盖 ingestion spec 的全部选项

INSERTREPLACE并不支持 ingestion spec 中的所有选项,典型缺失项包括:

  • 维度属性createBitmapIndexmultiValueHandling(见 dimension 对象);
  • tuningConfig 属性indexSpec(见 tuningConfig)。

不过请注意:在上下文参数层面,MSQ 提供了部分等效能力。例如 reference.md 的上下文参数表 中:

  • indexSpec可以作为MSQ 查询上下文参数INSERT/REPLACE)传入,用于段生成时的索引配置,可传 JSON 字符串或对象,包括前端编码(front coding) 配置;
  • segmentSortOrder可以控制段内行排序;
  • rowsPerSegment(默认 3,000,000)、rowsInMemory(默认 100,000)用于控制段生成时的内存与行数行为;
  • arrayIngestMode(默认mvd,建议array)决定 ARRAY 类型值的存储方式。

结论:若某项原生 ingestion spec 能力在 MSQ 中找不到对应上下文参数,说明该能力当前不受 SQL-based ingestion 支持,需要评估改用原生批量摄取任务。

EXTERN函数的已知问题

不支持 schemaless(无模式)维度

原生摄取中 inclusions / exclusions 提供的 schemaless 维度特性在 MSQ 中不可用。所有列及其类型必须通过EXTERN函数的signature参数显式声明。

EXTERN的签名 JSON 形式(见 reference.md 的 EXTERN 章节):

SELECT <column> FROM TABLE( EXTERN( '<Druid input source>', -- 任意 Druid 输入源(JSON 编码字符串) '<Druid input format>', -- 任意 Druid 输入格式(JSON 编码字符串) '<row signature>' -- JSON 编码的列描述符数组 ) )

其中 signature 是列描述符数组,每个描述符必须包含nametype,type 支持stringlongdoublefloat,用于把外部数据映射到 SQL 层。也可以使用带EXTEND子句的 SQL 风格写法,如(timestamp VARCHAR, metricType VARCHAR, value BIGINT)

实践建议:在摄取脚本中维护好外部文件的列清单与类型映射;当输入 schema 变化时需同步更新 signature,不能指望自动发现。

匹配大量文件时可能耗尽 controller 内存

EXTERN配合匹配大量文件的输入源时,可能耗尽controller 任务的可用内存。这与 controller 承担全局编排、收集统计信息的设计有关:文件数量越多,controller 侧需要维护的元数据越多。

实践建议

  • 将大文件集拆分为多个较小的EXTERN查询分批摄取;
  • 关注 controller 任务所在进程的内存配置,必要时提升其内存配额。

EXTERN只用于外部文件,访问 Druid 数据源请用FROM

EXTERN引用的是外部文件。如果要访问 Druid 自身的 datasource(例如做「库内转换」、基于现有表的INSERT ... SELECT),应使用FROM <datasource>,而不是EXTERN。详见 index.md 中关于 in-database transformation 的说明。

WINDOW函数的已知问题

MSQ 引擎对窗口函数有两项限制:

  1. 窗口内元素数量上限为 100,000。即单个窗口(window)中参与计算的行数不能超过 100,000 条。这与 MSQ 的 shuffle/帧(frame)设计相关——窗口计算需要把窗口内数据物化到内存中。上下文参数maxRowsMaterializedInWindow(定义见 MultiStageQueryContext.java)可用于控制窗口内物化的行数规模,规避该限制时需在「窗口划分粒度」与「内存占用」之间权衡。

  2. 为规避 MSQ 引擎中的leafOperators,窗口函数在窗口 stage 之后会附加一个额外的 scan stage(针对原生引擎存在非空leafOperator的场景)。这意味着带窗口函数的查询在计划中会比普通查询多一个执行阶段,可据此理解查询计划(explain)中多出的 stage,这属于正常现象而非故障。

配套参考:错误码与限制速查

本文涉及的已知问题最终都会落到 reference.md 的错误码表 与 Limits 表 上。与本文直接相关的条目汇总如下:

场景错误码关键字段 / 处置建议
GROUPING SETSQueryNotSupported改写为多个GROUP BY+UNION ALL
磁盘空间耗尽UnknownError(消息含 "No space left on device")扩容druid.indexer.task.baseDir所在磁盘
临时存储不足NotEnoughTemporaryStorage调大druid.indexer.task.tmpStorageBytesPerTask
controller 被杀Canceled检查进程稳定性,避免直接杀 controller
空结果 INSERT/REPLACE(当failOnEmptyInsert=trueInsertCannotBeEmpty默认failOnEmptyInsert=false,空 INSERT 为 no-op

建议阅读顺序:先读概念页理解 MSQ 执行模型,再结合参考手册查询语句语法、上下文参数与错误码,最后回到本文核对各项已知限制;示例页提供了可运行的实战查询。

总结

MSQ 引擎的已知问题可以归纳为四条主线:编排层(controller 无容错、大文件集撑爆 controller 内存)、存储层(worker 阶段输出占用druid.indexer.task.baseDir磁盘、临时存储上限)、SQL 语义层(无GROUPING SETS、无带列列表 INSERT/REPLACE、列按名称匹配、无 schemaless 维度、窗口上限 100,000)、能力边界层(ingestion spec 部分选项缺失)。规划 SQL-based ingestion 时,围绕这四条主线做容量预估、查询改写与错误码预案,即可把已知问题对生产链路的影响降到最低。

  • 数据库
  • OLAP
  • 大数据
  • 后端

【免费下载链接】druid

Apache Druid: a high performance real-time analytics database.

项目地址:https://gitcode.com/gh_mirrors/druid6/druid
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 11:38:24

无刷电机霍尔换向原理与STM32六步换向实战详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 11:38:15

基于Acrel-5000的公共建筑能耗管理系统设计与应用——以江苏涟水经济开发区集中供热项目为例

摘要&#xff1a;能源危机日益严峻的当下&#xff0c;大型公共建筑的能耗管理已成为亟待解决的现实难题。本文以江苏涟水经济开发区集中供热项目为背景&#xff0c;介绍一套基于Acrel-5000的能耗管理系统。系统通过智能电力仪表采集配电现场电参量&#xff0c;采用现场就地组网…

作者头像 李华
网站建设 2026/9/24 11:36:28

Pixhawk 2.4.8差速无人车从零下地:参数配置与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 11:35:32

博科光纤交换机初始化与Zoning配置实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 11:34:26

28 nm PMOS中SiGe外延应力调控四维优化方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华