十分钟鸟瞰Druid

Druid是个啥

Druid是一个分布式的列式数据存储,能够提供高效的数据查询。Druid主要解决的是对于大量的基于时序的数据进行聚合查询,它结合了OLAP、时序数据库、搜索系统等多方面的特性。

当数据可以实时摄入(”摄入“这个词就是写入到Druid中,后面还会经常用到这个词)之后立即可查。当数据提交之后是不可变,所以通常是基于时序的事实事件,事件发生之后被进入Druid,外部系统就可以对该事实进行查询以及聚合分析。Druid采用的架构,shared-nothing架构与lambda架构。

在Netflix、PayPal、ebay以及国内的阿里、头条、滴滴、快手等互联网公司Druid都被广泛引用。

Druid设计目

快速查询 : 部分数据聚合 + Cache + 索引

水平拓展能力:分布式列式数据存储+并行化查询

实时分析:数据不可变性、Append-Only特性

Druid特性

吞吐量大

流式数据摄入和实时分析

查询灵活

查询速度快

核心Node

Druid中有5大核心Node,分别是:Realtime Node、IndexingService Node(由Overlord、MiddleManager以及Peon)、Historical Node、Coordinator Node、Broker Node。

这里就从”上帝视角“快速了解数据的摄入流程。如下图所示,实时数据首先会写入到Realtime Node的内存中,毕竟内存空间有限,Realtime Node只能存储一个时间窗口的数据,该时间窗口之前的数据会通过IndexingService Node持久化成segment文件,并保存到Deep Storage中。

十分钟鸟瞰Druid
Druid数据流向图

除了实时数据之外,如上图所示,还会有批量数据写入到Druid集群,这些数据会直接通过IndexingService Node持久化为segment文件,并存储到Deep Storage中。

在segment被存储到Deep Storage的同时,还会将segment的元数据存储到Metadata Storage中。Coordinator Node中有一个定时任务,会定时同步Metadata Storage,从而感知新创建的segment文件。之后,Coordinator会通过Zookeeper通知Historical Node从Deep Storage中加载指定的新segment文件。

Historical Node

Historical Node会监听Zookeeper上相应的load queue path(其实就是一个普普通通的zk path,名字起得跟个队列一样,吓唬谁呢?),Coordinator会将新segment的元数据(其中包含segment文件的存储位置、如何解压以及如何处理该segment)写入到load queue path下。Historical Node在处理新segment时,会先检测本地是否已有该segment文件,如果没有,则从Deep Storage中加载并进行处理。完成segment文件的处理之后,Historical Node会在Zookeeper的served segments path下标记该segment由它对外提供查询。

Broker Node

从上图可以看出,Broker Node主要负责路由客户端发来的查询请求。更详细点地说,Broker Node会感知Zookeeper上的segment信息,从而得知每个segment文件在集群中的分布,Broker会为每个datasource创建一个timeline,timeline则是按照segment时间顺序排列其分布位置。在客户端发来的查询请求中会包含datasource以及时间范围,Broker Node会根据segment的分布将请求查分并交给多个Historical Node(或Realtime Node)完成查询。对于Historical Node返回的查询结果,Broker Node会进行缓存,而Realtime Node返回的查询结果,则不会进行缓存。(缓存必然是为了查询呀,不多说了)。

Coordinator Node

Coordinator Node主要负责管理segment,具体点说,就是负责控制Druid集群加载新segment、删除不符合规则的segment、管理segment副本以及控制segment在集群各个节点上的分布。为了实现Coordinator Node的高可用,Druid依赖Zookeeper选举出一个Coordinator Leader Node,其他Coordinator都是Follower。

在Coordinator Leader Node上的会启动一个定时任务(默认间隔一分钟),该定时任务会同步Zookeeper上segment信息以及Metadata Storage中记录的segment元数据。当发现新segment的时候,会将选择一个Historical Node,将segment写入到其在Zookeeper上对应的load queue下,Historical Node监听到之后会加载该segment。当发现失效的segment或是旧版本的segment,会暂时写入到remove list中,这只是个逻辑删除,最终会进行真正的物理删除。

当Coordinator Node检测到一个Historical Node宕机的时候,会将该Historical Node负责的segment交由其他Historical Node负责。当然,还有一些应对“网络抖动”场景(Historical Node与Coordinator Node之间的网络闪断、闪连)的参数配置,后面再说。

IndexingService Node

从Druid数据流向图上可以看出,进入Druid集群的数据都会由IndexingService处理生成segment。IndexingService包含三个核心部分:Overlord、MiddleManager、Peon。如下图所示,Overlord负责新任务(批量任务或是实时任务)的接收,当接收到新任务时,会将task信息注册到Zookeeper上。MiddleManager监听到这些新任务之后,会将task分配给不同的Peon执行,Peon是Indexing Service中任务的真正执行者,一个MiddleManager可以管理多个Peon实例。Peon在执行任务的过程中,会定期将任务执行状态更新到Zookeeper上,这样Overlord就可以了解任务的执行状态了。
十分钟鸟瞰Druid
Indexing Service架构图

与Coordinator Node类似,为了保证高可用,需要部署多个Overlord,其中一个Overlord会通过Zookeeper选举为Leader,其他的Overlord成为Follower。

Realtime Node

Realtime Node主要实现Stream pull,但是在官方文档上赫然一行大字:
十分钟鸟瞰Druid
行吧,不说了Realtime Node了,后面有空再说Kafka Indexing Service吧。╮(╯_╰)╭

Zookeeper

Druid依赖Zookeeper实现了分布式锁、Leader选举(例如,Overlord和Coordinator的选主)、分布式队列(例如,Coordinator与Historical之间的通信)等功能,可以说Druid是重度依赖Zookeeper的。

Deep Storage

常用的Deep Storage是HDFS,它是segment的持久化备份。通过前面介绍我们知道,Historical会加载,segment并对外提供查询,在某些场景中(例如Historical宕机),其他Historical可以从Deep Storage重新加载这些segment,并对外提供服务。

Metadata Storage

在单机版本中,Druid默认提供的是Derby作为Metadata Storage;在生产环境中则建议使用Mysql或是PostgreSQL。Metadata Storage中大约有10张表,这里简单介绍一下其中比较核心的表的功能。

segment table:记录当前集群中可用segment的元数据。Coordinator定时拉取的就是这个表。

rule table:记录segment的load/drop规则。Coordinator会根据这些规则决定Druid集群如何处理相应的segment。

task-related table:涉及多个表,Overlord和MiddleManager会通过这些表管理task。

config table:用于记录运行时的一些配置信息。

十分钟鸟瞰Druid

十分钟鸟瞰Druid