- 数据库
- 数据分析
- OLAP
- 大数据
- 实时分析
- 数据仓库
- 后端
【免费下载链接】druid
Apache Druid: a high performance real-time analytics database.
Druid 的原生查询语言是"基于 HTTP 的 JSON",所有查询都通过 HTTP REST 风格的请求发送到可查询节点(Broker、Historical 或 Realtime)。本文是 Druid 查询的总览手册:你将掌握查询的 HTTP 协议格式、九种原生查询类型的适用场景、查询 ID 的生成与取消机制,以及查询失败时的错误响应结构——这些知识是后续深入阅读各类查询文档、编写客户端或排查线上问题的基础。
Apache Druid 数据流架构示意图
查询的 HTTP 协议:JSON over HTTP
Druid 查询使用 HTTP REST 风格请求,发送到可查询节点(Broker、Historical 或 Realtime)。查询体以 JSON 表达,且这三种节点暴露的是同一个 REST 查询接口。
在正常运维场景下,查询应统一发往 Broker 节点;只有在排查特定节点行为或做内部调试时才直接查询 Historical / Realtime。发起查询的标准方式是使用curl向查询接口 POST 一个 JSON 文件:
curl -X POST '<queryable_host>:<port>/druid/v2/?pretty' -H 'Content-Type:application/json' -d @<query_json_file>几个关键点:
- 端口差异:Broker 与 Router 的默认 HTTP 端口是
8082,Historical 是8083,Realtime(实时索引任务)是8084。实际端口以各自节点的runtime.properties配置为准,例如 examples/conf/druid/broker/runtime.properties 中 Broker 的druid.port=8082。 ?pretty参数:可选,加上后返回的 JSON 会以缩进格式输出,便于阅读调试。Content-Type:请求头必须设置为application/json。Druid 同样支持 Smile 的@Produces/@Consumes声明中有明确体现。
从源码角度印证:io.druid.server.QueryResource类以@Path("/druid/v2/")注解暴露查询端点(见 QueryResource.java),其doPost方法接收输入流、用 JSON/Smile 对应的ObjectMapper反序列化为Query对象,再交给QuerySegmentWalker执行,最终以流式(StreamingOutput)方式把结果写回客户端。Historical 与 Peon 注册的是基础版QueryResource,而 Broker 注册的是其子类BrokerQueryResource(见 CliBroker.java 与 CliHistorical.java),后者额外提供了/druid/v2/candidates调试端点,用于查看某查询会被路由到哪些服务器(BrokerQueryResource.java)。
Druid 的原生查询是相对底层的,它与 Druid 内部的执行模型紧密对应,设计目标就是轻量、快速完成。这意味着对于更复杂的分析或更复杂的可视化,往往需要把多个 Druid 查询组合起来使用,而不是在一个查询里塞进全部逻辑。
说明:由于原生查询语言是 JSON over HTTP,社区已经为其他语言贡献了大量客户端库(Java、Python、JavaScript 等),方便以编程方式查询 Druid。
原生查询类型总览
Druid 针对不同使用场景提供了多种查询类型,每种类型由一组 JSON 属性构成,属性含义在各查询类型的专项文档中详细描述。所有查询类型共享queryType、dataSource、intervals等公共字段,其中queryType是 Druid 判断如何解释查询的第一依据。
| 分类 | 查询类型 | 核心用途 | 专项文档 |
|---|---|---|---|
| 聚合查询 | Timeseries | 按时间粒度做聚合,不按维度分组 | timeseriesquery.md |
| 聚合查询 | TopN | 对单个维度按指标排序取前 N 名 | topnquery.md |
| 聚合查询 | GroupBy | 最灵活的聚合查询,支持多维度分组与排序 | groupbyquery.md |
| 元数据查询 | Time Boundary | 返回数据源中数据的时间边界 | timeboundaryquery.md |
| 元数据查询 | Segment Metadata | 返回某个时间段内的 Segment 元数据 | segmentmetadataquery.md |
| 元数据查询 | Datasource Metadata | 返回数据源的元数据(如更新时间戳) | datasourcemetadataquery.md |
| 搜索查询 | Search | 返回匹配搜索规范的维度值 | searchquery.md |
所有查询都共享一组上下文参数(query context),用于配置超时、优先级、缓存、调试行为等,详见下文"查询上下文"一节;dataSource、granularity、filter、intervals等公共概念则分别由 datasource.md、granularities.md、filters.md 等文档定义。
下面通过三个示例快速感受查询 JSON 的形态。
Timeseries 查询——按小时聚合page_views数据源中的added指标:
{ "queryType": "timeseries", "dataSource": "page_views", "granularity": "hour", "aggregations": [ { "type": "count", "name": "rows" }, { "type": "longSum", "name": "added", "fieldName": "added" } ], "intervals": [ "2015-09-12T00:00:00.000Z/2015-09-13T00:00:00.000Z" ] }TopN 查询——按维度page分组、以added求和降序取前 10:
{ "queryType": "topN", "dataSource": "page_views", "granularity": "all", "dimension": "page", "metric": "added", "threshold": 10, "aggregations": [ { "type": "longSum", "name": "added", "fieldName": "added" } ], "intervals": [ "2015-09-12T00:00:00.000Z/2015-09-13T00:00:00.000Z" ] }Search 查询——在dim1、dim2上查找包含子串Ke的维度值(不区分大小写),完整字段说明见 searchquery.md:
{ "queryType": "search", "dataSource": "sample_datasource", "granularity": "day", "searchDimensions": [ "dim1", "dim2" ], "query": { "type": "insensitive_contains", "value": "Ke" }, "sort": { "type": "lexicographic" }, "intervals": [ "2013-01-01T00:00:00.000/2013-01-03T00:00:00.000" ] }如何选择查询类型
在可能的情况下,官方推荐优先使用Timeseries和TopN,而不是 GroupBy。三者的定位差异非常明确:
| 场景 | 推荐查询 | 原因 |
|---|---|---|
| 只需要按时间聚合、不需要按维度分组 | Timeseries | 远快于 GroupBy |
| 需要按单个维度分组并排序取前 N | TopN | 针对单维度做了大量优化,明显优于 GroupBy |
| 需要多维度分组、复杂排序、过滤后的深度分析 | GroupBy | 最灵活,但性能也最差 |
简而言之:GroupBy 是 Druid 最灵活的查询,但也是性能最差的。如果查询不需要维度分组,请选择 Timeseries;如果只需要单个维度上的排序取 topN,请选择 TopN。
需要指出,上述"灵活性最高、性能最差"是相对同仓库内 Timeseries / TopN 实现而言的官方建议(原文出处见 querying.md),并非与其他数据库产品的横向对比。TopN 的实现之所以高效,是因为它采用了"分段局部 topN + 全局归并"的策略:每个数据段先各自算出局部 topN(段内结果数由上下文参数minTopNThreshold控制,默认 1000),再把局部结果归并得到全局 topN——这与 GroupBy 需要维护全量分组哈希表的开销形成鲜明对比。
查询上下文(Query Context)
查询 JSON 中可以携带一个context对象,用于配置各项运行参数。以下参数适用于所有查询类型(完整定义见 query-context.md):
| 属性 | 默认值 | 说明 |
|---|---|---|
timeout | 0(无超时) | 查询超时时间(毫秒),超过后未完成的查询将被取消。 |
priority | 0 | 查询优先级,高优先级查询在计算资源竞争中获得优先处理。 |
queryId | 自动生成 | 查询的唯一标识;若已设置或已知,可用于取消查询(见下文)。 |
useCache | true | 是否使用查询缓存,可在 Broker / Historical 节点配置中覆盖。 |
populateCache | true | 是否将查询结果写入查询缓存,主要用于调试;可在节点配置中覆盖。 |
bySegment | false | 是否按数据段(segment)返回结果,主要用于调试,开启后返回结果会附带来源段信息。 |
finalize | true | 是否"终结"聚合结果,主要用于调试。例如置为false时hyperUnique聚合器返回完整的 HyperLogLog sketch 而非预估基数。 |
chunkPeriod | P0D(关闭) | 仅 Broker 有效:长区间查询会被拆分为较短区间的子查询并行归并。使用 ISO 8601 周期,例如设为P1M时覆盖一年的查询会被拆成 12 个小查询。拆分后的查询会占用更多集群资源,但可能显著更快。注意 Broker 使用查询处理线程池发起分块,因此需保证 Broker 的druid.processing.numThreads配置充足。GroupBy 默认不支持chunkPeriod(使用旧的 v1 引擎时除外)。 |
各查询类型还有专属上下文参数:TopN 有minTopNThreshold(默认1000,控制每个段返回参与全局归并的局部结果数);Timeseries 有skipEmptyBuckets(默认false,置为true可关闭零填充,只返回有结果的桶);GroupBy 的上下文参数见 groupbyquery.md。
从源码实现看,timeout与queryId的处理在 QueryResource.java 中:请求到达后,若查询未携带queryId,服务端会为其生成一个 UUID;若未携带timeout,则默认使用ServerConfig.getMaxIdleTime()对应的毫秒数作为超时。这也解释了为什么即使客户端不设置上下文,服务端也能对查询做超时控制与后续取消。
查询取消(Query Cancellation)
Druid 支持通过查询的唯一标识显式取消查询。只要在发起查询时设置了queryId(或该 ID 已知),就可以在Broker 或 Router上调用如下端点取消:
DELETE /druid/v2/{queryId}例如,假设查询 ID 为abc123:
curl -X DELETE "http://host:port/druid/v2/abc123"底层的取消链路非常清晰:
QueryResource的getServer方法映射@DELETE @Path("{id}")(QueryResource.java),调用QueryManager.cancelQuery(queryId),成功后返回 HTTP 202(ACCEPTED);- QueryManager.java 内部以
SetMultimap<String, ListenableFuture>维护queryId → 查询 Future的映射,cancelQuery取出该 ID 关联的所有ListenableFuture并逐个执行future.cancel(true)(以中断方式取消),同时在查询完成监听器中自动清理映射条目; - 查询真正被取消时,执行端会抛出
CancellationException,最终被包装为error: "Query cancelled"的错误响应(见下文)。
安全说明:从 QueryResource.java 的实现可以看到,若集群开启了鉴权(AuthConfig.isEnabled()),取消请求需要携带有效的授权信息,且调用者必须对查询涉及的每个数据源拥有WRITE权限(授权机制见 AuthConfig 相关实现 以及 docs/content/design/coordinator.md 中的安全配置指引),否则会返回 403FORBIDDEN。
查询错误与响应结构
如果查询执行失败,节点会返回HTTP 500响应,响应体是一个 JSON 对象,结构如下:
{ "error" : "Query timeout", "errorMessage" : "Timeout waiting for task.", "errorClass" : "java.util.concurrent.TimeoutException", "host" : "druid1.example.com:8083" }各字段含义:
| 字段 | 说明 |
|---|---|
error | 定义良好的错误码(取值见下表)。 |
errorMessage | 关于错误的自由格式信息,可能为 null。 |
errorClass | 引发错误的异常类,可能为 null。 |
host | 错误发生的节点主机名,可能为 null。 |
error字段可能的取值:
| 错误码 | 说明 |
|---|---|
Query timeout | 查询超时。 |
Query interrupted | 查询被中断,可能由 JVM 关闭等原因导致。 |
Query cancelled | 查询通过取消 API 被取消。 |
Resource limit exceeded | 查询超过了配置的资源限制(例如 groupBy 的maxResults)。 |
Unknown exception | 其他异常。请查看errorMessage和errorClass获取详情,但注意这两个字段是自由格式的,内容可能随版本变化。 |
源码级印证:错误响应的 JSON 序列化由io.druid.query.QueryInterruptedException完成(QueryInterruptedException.java)。这个类虽然名字叫"Interrupted",但实际上是客户端侧所有查询失败的统一表示:它通过@JsonProperty注解把error、errorMessage、errorClass、host四个字段序列化为上述 JSON 结构。其错误码推导逻辑(getErrorCodeFromThrowable)正是按异常类型映射到上表:
TimeoutException→Query timeoutInterruptedException→Query interruptedCancellationException→Query cancelledResourceLimitExceededException→Resource limit exceeded- 其余所有异常 →
Unknown exception
在服务端,QueryResource.java 的gotError方法通过QueryInterruptedException.wrapIfNeeded(e)把任意异常包装成统一格式并返回Response.serverError()(即 HTTP 500)。客户端侧,Broker 的DirectDruidClient会反序列化并包装这些错误对象(见 DirectDruidClient.java 对druid/v2的请求处理),从而把集群内部的错误逐层传递回调用方。此外,QueryResource.java 会在错误发生时向监控系统发射query/time(success=false)指标并写入请求日志,便于事后排查。
进阶指引
本文覆盖了 Druid 查询的公共协议与通用机制。当你需要编写具体查询时,建议按以下路径深入:
- 公共概念:datasource.md(数据源定义)、granularities.md(时间粒度)、filters.md(过滤)、dimensionspecs.md(维度规范)、aggregations.md(聚合器)、post-aggregations.md(后聚合)、limitspec.md(排序与截断)。
- 各查询类型:分别阅读 timeseriesquery.md、topnquery.md、groupbyquery.md、searchquery.md 以及三个元数据查询文档。
- 高级能力:joins.md(查询时关联)、multitenancy.md(多租户)、caching.md(缓存行为)、query-context.md(上下文参数全集)。
- 其他访问方式:Druid 还提供了 SQL 查询层;如果你需要了解各服务节点的职责与配置,可阅读 Broker 设计文档 与 Historical 设计文档。
- 数据库
- 数据分析
- OLAP
- 大数据
- 实时分析
- 数据仓库
- 后端
【免费下载链接】druid
Apache Druid: a high performance real-time analytics database.
相关推荐
Apache Druid 查询执行机制完全指南:从 Datasource 类型到 scatter-gather 与子查询限制
Apache Druid 查询执行机制完全指南:从 Datasource 类型到 scatter gather 与子查询限制 本文以 Apache Druid
数据库OLAP大数据后端Apache Pulsar SQL REST API 实战指南:基于 Trino/Presto HTTP 协议提交与轮询查询
Apache Pulsar SQL REST API 实战指南:基于 Trino/Presto HTTP 协议提交与轮询查询 Apache Pulsar SQL
消息队列后端流处理Apache Druid 指标监控完整指南:查询、摄取与协调 Metrics 详解
Apache Druid 指标监控完整指南:查询、摄取与协调 Metrics 详解 本文以 Apache Druid 官方运维文档为核心,系统讲解 Druid
数据库数据分析OLAP大数据实时分析数据仓库后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考