- 数据库
- OLAP
- 大数据
- 后端
【免费下载链接】druid
Apache Druid: a high performance real-time analytics database.
导读
本文基于 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 语句作为批处理任务的多阶段查询任务引擎。该引擎支持用INSERT、REPLACE语句批量装载数据,并实验性地支持把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不能与显式设置为false的durableShuffleStorage同时使用,启用 fault tolerance 前必须在服务器级别配置druid.msq.intermediate.storage.enable=true的持久化存储。
相关的重启次数限制也记录在 reference.md 的 Limits 表 中:
| 限制项 | 值 | 超限错误码 |
|---|---|---|
单个 worker 的最大重启次数(首次运行不计,即最多运行 1 +workerRelaunchLimit次) | 2 | TooManyAttemptsForWorker |
| 整个作业跨所有 worker 的最大重启次数 | 100 | TooManyAttemptsForJob |
实践建议: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"(对应错误码UnknownError的message字段,见 reference.md)。
这与另一个限制相关联:MSQ 引擎还有按 worker 的临时存储上限druid.indexer.task.tmpStorageBytesPerTask,若配置的临时存储不足以启动某个 stage,会返回NotEnoughTemporaryStorage错误(错误详情字段suggestedMinimumStorage与configuredTemporaryStorage,见 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+ 常规聚合表达式。
INSERT与REPLACE语句的已知问题
不支持带列列表的 INSERT / REPLACE
形如INSERT INTO tbl (a, b, c) SELECT ...的带列列表写法未实现。这是与标准 SQL 的一个重要差异,规划查询时不要照搬传统数据库的写法。
列按「名称」而非「位置」匹配
INSERT ... SELECT和REPLACE ... 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 的全部选项
INSERT和REPLACE并不支持 ingestion spec 中的所有选项,典型缺失项包括:
- 维度属性:
createBitmapIndex与multiValueHandling(见 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 是列描述符数组,每个描述符必须包含name和type,type 支持string、long、double、float,用于把外部数据映射到 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 引擎对窗口函数有两项限制:
窗口内元素数量上限为 100,000。即单个窗口(window)中参与计算的行数不能超过 100,000 条。这与 MSQ 的 shuffle/帧(frame)设计相关——窗口计算需要把窗口内数据物化到内存中。上下文参数
maxRowsMaterializedInWindow(定义见 MultiStageQueryContext.java)可用于控制窗口内物化的行数规模,规避该限制时需在「窗口划分粒度」与「内存占用」之间权衡。为规避 MSQ 引擎中的
leafOperators,窗口函数在窗口 stage 之后会附加一个额外的 scan stage(针对原生引擎存在非空leafOperator的场景)。这意味着带窗口函数的查询在计划中会比普通查询多一个执行阶段,可据此理解查询计划(explain)中多出的 stage,这属于正常现象而非故障。
配套参考:错误码与限制速查
本文涉及的已知问题最终都会落到 reference.md 的错误码表 与 Limits 表 上。与本文直接相关的条目汇总如下:
| 场景 | 错误码 | 关键字段 / 处置建议 |
|---|---|---|
GROUPING SETS | QueryNotSupported | 改写为多个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=true) | InsertCannotBeEmpty | 默认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.
相关推荐
Apache Druid SQL-based Ingestion API 实战指南:用 MSQ 任务引擎提交与运维 INSERT/REPLACE 任务
Apache Druid SQL based Ingestion API 实战指南:用 MSQ 任务引擎提交与运维 INSERT/REPLACE 任务 导读 本
数据库OLAP大数据后端gVisor限制说明:已知限制与不兼容特性
gVisor限制说明:已知限制与不兼容特性 概述 gVisor作为容器应用内核,虽然提供了强大的安全隔离能力,但在追求安全性的同时也存在一些已知的限制和不兼容特
云原生容器运行时操作系统应用安全Apache Druid 多阶段查询(MSQ)任务引擎:SQL 批量摄取完整指南
Apache Druid 多阶段查询(MSQ)任务引擎:SQL 批量摄取完整指南 本文围绕 Apache Druid 内置的 druid multi stage
数据库OLAP大数据后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考