导读:本文将描述 Apache Druid 的基本集群架构,说明架构中各进程的做用。并从数据写入和数据查询两个角度来讲明 Druid 架构的工做流程。html
关注公众号 MageByte,设置星标点「在看」是咱们创造好文的动力。公众号后台回复 “加群” 进入技术交流群获更多技术成长。算法
Druid 是多进程架构,每种进程类型均可以独立配置,独立扩展。这样能够为集群提供最大的灵活度。这种设计还提供了强失效容忍:一个失效的组件不会当即影响另外的组件。sql
下面咱们来深刻了解 Druid 有哪些进程类型,每种进程又在整个集群中扮演什么角色。数据库
Druid 有多种进程类型,以下:apache
你能够以任何方式来部署上面的进程。可是为了易于运维,官方建议如下面三种服务类型来组织进程:Master、Query 和 Data。服务器
除了内置的进程类型,Druid 还有三个外部依赖项。网络
共享文件存储,只要配置成容许 Druid 访问便可。在集群部署中,一般使用分布式存储(如 S3 或 HDFS)或挂载网络文件系统。在单机部署中,一般使用本地磁盘。Druid 使用 Deep Storage 存储写入集群的数据。架构
Druid 仅将 Deep Storage 用做数据的备份,并做为 Druid进程间在后台的数据传输方式。要响应查询,Historical 进程并不从 Deep Storage 上读取数据,在任何查询以前,先从本地磁盘查询已经存在的数据。这意味着,Druid 在查询时并不须要访问 Deep Storage,这样就能够获得最优的查询延迟。这也意味着,在 Deep Storage 和 Historical 进程间你必须有足够的磁盘空间来存储你计划加载的数据。app
Deep Storage 是 Druid 弹性、容错设计的重要组成部分。若是 Druid 单机进程本地数据丢失,能够从 Deep Storage 恢复数据。运维
元数据存储,存储各类共享的系统元数据,如 segment 可用性信息和 task 信息。在集群部署中,一般使用传统的 RDBMS,如 PostgreSQL 或 MySQL。在单机部署中,一般使用本地存储,如 Apache Derby 数据库。
用来进行内部服务发现,协调,和主选举。
下图能够看出使用官方建议的 Master/Query/Data 服务部署方式,查询和写入数据是如何进行的:
Druid 数据存储在"datasources"中,它就像 RDBMS 中的 table。每个 datasources 经过时间分区,或经过其余属性进行分区。每个时间范围称之为"chunk"(好比,一天一个,若是你的 datasource 使用 day 分区)。在 chunk 中,数据被分区进一个或多个"segments"中。每个 segment 是一个单独的文件,一般包含数百万行数据。一旦 segment 被存储进 chunks,其组织方式将如如下时间线所示:
一个 datasource 也许只有一个,也可能有数十万甚至上百万个 segment。每一个 segment 生命周期开始于 MiddleManager 建立时,刚被建立时,segment 是可变和未提交的。segment 构建过程包含如下几步,旨在生成结构紧凑并支持快速查询的数据文件。
segment 定时提交和发布。此时,数据被写入 Deep Storage,而且再不可变,并从 MiddleManagers 进程迁移至 Historical 进程中。一个关于 segment 的 entry 将写入 metadata storage。这个 entry 是关于 segment 的元数据的自描述信息,包含如 segment 的数据模式,大小,Deep Storage 地址等信息。这些信息让 Coordinator 知道集群中哪些数据是可用的。
indexing 是每一个 segment 建立的机制。handoff 是数据被发布并开始能够被 Historical 进程处理的机制。这机制在 indexing 侧的工做顺序以下:
这机制在 Coordinator/Historical 侧的工做以下:
数据写入(indexing)和移交(handoff):
Segment 标识由下面四部分组成:
segmentGranularity
指定参数)。例如,这是 datasource 为clarity-cloud0
,时间段为2018-05-21T16:00:00.000Z/2018-05-21T17:00:00.000Z
,版本号为2018-05-21T15:56:09.909Z
,分区号为 1 的标识符:
clarity-cloud0_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T15:56:09.909Z_1
分区号为 0(块中的第一个分区)的 segment 省略了分区号,如如下示例所示,它是与前一个分区在同一时间块中的 segment,但分区号为 0 而不是 1:
clarity-cloud0_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T15:56:09.909Z
你可能想知道上一节中描述的“版本号”是什么。
Druid 支持批处理模式覆写。在 Driud 中,若是你要作的只是追加数据,那么每一个时间块只有一个版本。可是,当你覆盖数据时,在幕后发生的事情是使用相同的数据源,相同的时间间隔,但版本号更高的方式建立了一组新的 segment。这向 Druid 系统的其他部分发出信号,代表应从群集中删除较旧的版本,而应使用新版本替换它。
对于用户而言,切换彷佛是瞬间发生的,由于 Druid 经过先加载新数据(但不容许对其进行查询)来处理此问题,而后在全部新数据加载完毕后,当即将新查询切换到新 segment。而后,它在几分钟后删除旧 segment。
每一个 segment 的生命周期都涉及如下三个主要领域:
元数据存储区
中。将 segmnet 的记录插入元数据存储的操做称为发布。而后将元数据中的use
布尔值设置成可用
。由实时任务建立的 segment 将在发布以前可用,由于它们仅在 segment 完成时才发布,而且不接受任何其余数据。你可使用 Druid SQL sys.segments
表检查当前 segment 的状态 。它包括如下标志:
is_published
:若是 segment 元数据已发布到存储的元数据中,used
则为 true,此值也为 true。is_available
:若是该 segment 当前可用于实时任务或Historical查询,则为 True。is_realtime
:若是 segment 在实时任务上可用,则为 true 。对于使用实时写入的数据源,一般会先设置成true
,而后随着 segment 的发布和移交而变成false
。is_overshadowed
:若是该 segment 已发布(used
设置为 true)而且被其余一些已发布的 segment 彻底覆盖,则为 true。一般,这是一个过渡状态,处于此状态的 segment 很快就会将其used
标志自动设置为 false。查询首先进入Broker
进程,Broker
将得出哪些 segment 具备与该查询有关的数据(segment 列表始终按时间规划,也能够根据其余属性来规划,这取决于数据源的分区方式),而后,Broker
将肯定哪些 Historical
和 MiddleManager
正在为这些 segment 提供服务,并将重写的子查询发送给每一个进程。Historical
/ MiddleManager
进程将接受查询,对其进行处理并返回结果。Broker
接收结果并将它们合并在一块儿以获得最终答案,并将其返回给客户端。
Broker
会分析每一个请求,优化查询,尽量的减小每一个查询必须扫描的数据量。相比于 Broker 过滤器作的优化,每一个 segment 内的索引结构容许 Druid 在查看任何数据行以前先找出哪些行(若是有)与过滤器集匹配。一旦 Druid 知道哪些行与特定查询匹配,它就只会访问该查询所需的特定列。在这些列中,Druid 能够在行与行之间跳过,从而避免读取与查询过滤器不匹配的数据。
所以,Druid 使用三种不一样的技术来优化查询性能:
检索每一个查询需访问的 segment。
在每一个 segment 中,使用索引来标识查询的行。
其余系列文章连接:
想了解更多数据存储,时间序列,Druid 的知识,可关注个人公众号。点「在看」是咱们创造好文的动力