Druid架构

目录

 

1.Druid总体架构

1.1整体架构

1.2三条线路

2.实时节点

2.1实时索引原理

2.1.1Ingest阶段

2.2.2Persist阶段

2.2.3 Merge阶段

2.2.4Handoff阶段

3.历史节点

4.协调节点

5.Broker节点

6.Indexer节点

7.ZooKeeper 

 

参考文章


1.Druid总体架构

1.1整体架构

 

 

Druid 本身包含了五种节点 : Realtime、Historical、Coordinator、Broker、Indexer

  • Historical 历史节点是进行存储和查询的“历史”数据(非实时)的工作区,它会从深存储区(Deep Storage)中加载数据段(Data/Segments),响应 Broker 节点的查询请求并返回结果。历史节点通常会在本机同步深存储区上的部分数据段,所以即使深存储区不可访问了,历史节点还是能查询到已经同步的数据段。

  • Realtime 实时节点是进行存储和查询实时数据的工作区,它也会响应Broker节点的查询请求并返回结果 。实时节点会定期地将数据建立成数据段移到历史节点中。

  • Coordinator 协调节点可以认为是Druid中的master,它通过Zookeeper管理历史节点和实时节点,且通过Mysql中的metadata管理数据段。

  • Broker节点负责响应外部的查询请求,通过查询Zookeeper将请求分别转发给历史节点和实时节点,最终合并并返回查询结果给外部, 由Broker节点通过zookeeper决定哪些历史节点和实时节点提供服务。

  • Indexer 索引节点负责数据导入,加载批次和实时数据到系统中,并可以修改存储到系统中的数据 。Overlord Node (Indexing Service) Overlord会形成一个加载批处理和实时数据到系统中的集群,同时会对存储在系统中的数据变更(也称为索引服务)做出响应。另外,还包含了Middle Manager和Peons,一个Peon负责执行单个task,而Middle Manager负责管理这些Peons。

 

Druid 包含3个外部依赖 :Mysql、Deep storage、Zookeeper

 

  • Mysql:存储关于Druid中的metadata而不是存储实际数据,包含3张表:”druid_config”(通常是空的), “druid_rules”(协作节点使用的一些规则信息,比如哪个segment从哪个node去load)和“druid_segments”(存储每个segment的metadata信息);

  • Deep storage: 存储segments,Druid目前已经支持本地磁盘,NFS挂载磁盘,HDFS,S3等。Deep Storage的数据有2个来源,一个是批数据摄入, 另一个来自实时节点;

  • ZooKeeper: 被Druid用于管理当前cluster的状态,比如记录哪些segments从实时节点移到了历史节点;

     

1.2三条线路

Druid的整体架构如上图所示,其中主要有3条路线:

  1. 实时摄入的过程: 实时数据会首先按行摄入Real-time Nodes,Real-time Nodes会先将每行的数据加入到1个map中,内存增量索引ConcurrentSkipMap,等达到一定的行数或者大小限制时,Real-time Nodes 就会将内存中的map 持久化到磁盘中,持久化的倒排索引,Real-time Nodes 会按照segmentGranularity将一定时间段内的小文件merge为一个大文件,生成Segment,然后将Segment上传到Deep Storage(HDFS,S3)中,Coordinator知道有Segment生成后,会通知相应的Historical Node下载对应的Segment,并负责该Segment的查询。

  2. 离线摄入的过程: 离线摄入的过程比较简单,就是直接通过MR job 生成Segment,剩下的逻辑和实时摄入相同:

  3. 用户查询过程: 用户的查询都是直接发送到Broker Node,Broker Node会将查询分发到Real-time节点和Historical节点,然后将结果合并后返回给用户。

     

Segment采用的是类LSM树结构的列式存储,类LSM树结构和LSM树结构不同的是省去了WAL(预写日志)部分(疑问:不会有数据丢失吗?),通常会被上传到HDFS或S3上做深存储。相比行式存储,列式存储可以更加高效的使用CPU,因为加载时,按需取数据,只加载需要的列即可,不必把行中部分不相关的数据都加载进来;另外,列出存储也可以针对不同列字段类型采用不同的压缩算法,更加节省内存和磁盘空间。

2.实时节点

LSM-tree显然比较适合那些数据插入操作远多于数据更新删除操作与读操作的场景,同时 Druid在一开始就是为时序数据场景设计的,而该场景正好符合 LSM-tree的优势特点,因此 Druid架构便顺理成章地吸取了 LSM-tree的思想。

Druid的类 LSM-tree架构中的实时节点( Realtime Node)负责消费实时数据,与经典LSM-tree架构不同的是, Druid不提供日志及实行 WAL原则,实时数据首先会被直接加载进实时节点内存中的堆结构缓存区 (相当于 memtable),当条件满足时,缓存区里的数据会被冲写到硬盘上形成一个数据块 (Segment Split),同时实时节点又会立即将新生成的数据块加载到内存中的非堆区,因此无论是堆结构缓存区还是非堆区里的数据,都能够被查询节点(Broker Node)查询。

实时节点数据块的生成示意图

同时,实时节点会周期性地将磁盘上同一个时间段内生成的所有数据块合并为一个大的数据块( Segment)。这个过程在实时节点中叫作 SegmentMerge操作,也相当于 LSM-tree架构中的数据合并操作( Compaction)。合并好的 Segment会立即被实时节点上传到数据文件存储库( DeepStorage)中,随后协调节点( CoordinatorNode)会指导一个历史节点( Historical Node)去文件存储库,将新生成的 Segment下载到其本地磁盘中。当历史节点成功加载到 Segment后,会通过分布式协调服务( Coordination)在集群中声明其从此刻开始负责提供该 Segment的查询,当实时节点收到该声明后也会立即向集群声明其不再提供该 Segment的查询,接下来查询节点会转从该历史节点查询此 Segment的数据。而对于全局数据来说,查询节点会同时从实时节点(少量当前数据)与历史节点(大量历史数据)分别查询,然后做一个结果的整合,最后再返回给用户。 Druid的这种架构安排实际上也在一定程度上借鉴了命令查询职责分离模式( Command QueryResponsibility Segregation,CQRS)——这也是 Druid不同于 HBase等 LSM-tree系架构的一个显著特点。Druid对命令查询职责分离模式( CQRS)的借鉴如下图。

Druid对命令查询职责分离模式(CQRS)的借鉴

Druid的上述架构特点为其带来了如下显著的优势。 

  • 类 LSM-tree架构使得 Druid能够保证数据的高速写入,并且能够提供比较快速的实时查询,这十分符合许多时序数据的应用场景。 

  • 由于 Druid在设计之初就不提供对已有数据的更改,以及不实现传统 LSM-tree架构中普遍应用的 WAL原则,虽然这样导致了 Druid不适应于某些需要数据更新的场景,也降低了数据完整性的保障,但 Druid相对其他传统的 LSM-tree架构实现来说也着实减少了不少数据处理的工作量,因此让自己在性能方面更胜一筹。 

  • Druid对命令查询职责分离模式的借鉴也使得自己的组件职责分明、结构更加清晰明了,方便针对不同模块进行针对性的优化。

 

首先,无论是实时数据还是批量数据在进入Druid前都需要经过Indexing Service这个过程。在Indexing Service阶段,Druid主要做三件事:第一,将每条记录转换为列式(columnar format);第二,为每列数据建立位图索引;第三,使用不同的压缩算法进行压缩,其中默认使用LZ4,对于字符类型列采用字典编码(Dictionary encoding)进行压缩,对于位图索引采用Concise/Roaring bitmap进行编码压缩。最终的输出结果也就是Segment。

 

2.1实时索引原理

Druid实时索引过程有三个主要特性:

  • 主要面向流式数据(Event Stream)的摄取(ingest)与查询,数据进入Real-TimeNode后可进行即席查询。

  • 实时索引面向一个小的时间窗口,落在窗口内的原始数据会被摄取,窗口外的原始数据则会被丢弃,已完成的Segments会被Handoff到HistoricalNode。

  • 虽然Druid集群内的节点是彼此独立的,但是整个实时索引过程通过Zookeeper进行协同工作。

实时索引过程可以划分为以下四个阶段:

2.1.1Ingest阶段

Real-TimeNode对于实时流数据,采用LSM-Tree(Log-Structured Merge-Tree )将数据持有在内存中(JVM堆中),优化数据的写入性能。图3.29中,Real-TimeNodes在13:37申明服务13:00-14:00这一小时内的所有数据。

2.2.2Persist阶段

当到达一定阈值(0.9.0版本前,阈值是500万行或10分钟,为预防OOM,0.9.0版本后,阈值改为75000行或10分钟)后,内存中的数据会被转换为列式存储物化到磁盘上,为了保证实时窗口内已物化的Smoosh文件依然可以被查询,Druid使用内存文件映射方式(mmap)将Smoosh文件加载到直接内存 中,优化读取性能。如图3.29中所示,13:47、13:57、14:07都是Real-TimeNodes物化数据的时间点。

图2.1实时索引任务期间的内存情况与即席查询

图2.1描述了Ingest阶段与Persist阶段内数据流走向以及内存情况。Druid对实时窗口内数据读写都做了大量优化,从而保证了实时海量数据的即席可查。

2.2.3 Merge阶段

对于Persist阶段,会出现很多Smoosh碎片,小的碎片文件会严重影响后期的数据查询工作,所以在实时索引任务周期的末尾(略少于SegmentGranularity+WindowPeriod时长),每个Real-TimeNode会产生back-groundtask,一方面是等待时间窗口内“掉队”的数据,另一方面搜索本地磁盘所有已物化的Smoosh文件,并将其拼成Segment,也就是我们最后看到的index.zip。图3.29中,当到达索引任务末期14:10分时,Real-TimeNodes开始merge磁盘上的所有文件,生成Segment,准备Handoff。

图2.2实时任务各周期

2.2.4Handoff阶段

本阶段主要由CoordinatorNodes负责,CoordinatorNodes会将已完成的Segment信息注册到元信息库、上传DeepStorage,并通知集群内HistoricalNode去加载该Segment,同时每隔一定时间间隔(默认1分钟)检查Handoff状态,如果成功,Real-TimeNode会在Zookeeper中申明已不服务该Segment,并执行下一个时间窗口内的索引任务;如果失败,CoordinatorNodes会进行反复尝试。图3.29中,14:11分完成Handoff工作后,该Real-TimeNode申明不再为此时间窗口内的数据服务,开始下一个时间窗口内的索引任务。

 

3.历史节点

历史节点遵循shared-nothing的架构,因此节点间没有单点问题。节点间是相互独立的并且提供的服务也是简单的,它们只需要知道如何加载、删除和处理Segment。类似于实时节点,历史节点在Zookeeper中通告它们的在线状态和为哪些数据提供服务。加载和删除segment的指令会通过Zookeeper来进行发布,指令会包含segment保存在deep storage的什么地方和怎么解压、处理这些segment的相关信息。

 

 

如上图,在历史节点从深存储区下载某一segment之前,它会先检查本地缓存信息中看segment是否已经存在于节点中,如果segment还不存在缓存中,历史节点会从深存储区下载segment到本地。这阶段处理完成,这个segment就会在Zookeeper中进行通告。此时,这个segment就可以被查询了,查询之前需要将segment加载到内存中。

内存空间富裕,查询时需要从磁盘加载数据的次数就少,查询速度就快;反之,查询时需要从磁盘加载数据的次数就多,查询数据就相对慢。因此,原则上历史节点的查询速度与其内存空间大和所负责的Segment

数据文件大小成正比关系。

 

4.协调节点

 

协调节点主要负责Segment的管理和在历史节点上的分布。协调节点告诉历史节点加载新数据、卸载过期数据、复制数据、和为了负载均衡移动数据。Druid为了维持稳定的视图,使用一个多版本的并发控制交换协议来管理不可变的segment。如果任何不可变的segment包含的数据已经被新的segment完全淘汰了,则过期的segment会从集群中卸载掉。协调节点会经历一个leader选举的过程,来决定由一个独立的节点来执行协调功能,其余的协调节点则作为冗余备份节点。

 

5.Broker节点

Broker节点是历史节点和实时节点的查询路由。Broker节点知道发布于Zookeeper中的segment的信息,Broker节点就可以将到来的查询请求路由到正确的历史节点或者是实时节点,Broker节点也会将历史节点和实时节点的局部结果进行合并,然后返回最终的合并后的结果给调用者。Broker节点包含一个支持LRU失效策略的缓存。

如上图,每次Broker节点接收到查询请求时,都会先将查询映射到一组segment中去。这一组确定的segment的结果可能已经存在于缓存中,而不需要重新计算。对于那些不存在于缓存的结果,Broker节点会将查询转发到正确的历史节点和实时节点中去,一旦历史节点返回结果,Broker节点会将这些结果缓存起来以供以后使用,这个过程如图6所示。实时数据永远不会被缓存,因此查询实时节点的数据的查询请求总是会被转发到实时节点上去。实时数据是不断变化的,因此缓存实时数据是不可靠的。

Druid也使用了Cache机制来提高自己的查询效率。Druid提供了两类介质作为Cache以供选择。

  • 外部Cache,比如Memcached。

  • 本地Cache,比如查询节点或历史节点的内存作为Cache。

 

 

6.Indexer节点

索引服务是运行索引任务相关的高可用性,分布式的服务。索引服务创建(有时破坏)Druid的Segment。索引服务有一个类似主/从的架构。

 

 

 

索引服务是由三个主要部分组成:可以运行单个任务的peon组件,用于管理peon的中层管理组件,以及管理任务分配到中层管理组件的overlord组件。overlord组件和中层管理组件可以在同一节点上或跨多个节点上运行,而中层管理组件和peon组件总是相同的节点上运行。

 

7.ZooKeeper 

Druid 使用ZooKeeper(ZK)管理当前集群状态,在ZK上发生的操作有:

 

1.协调节点的leader选举

2.历史和实时节点发布segment协议

3.协调节点和历史节点之间的segment Load/Drop协议

4.overlord的leader选举

5.索引服务任务管理

 

 

 

 

 

 

 

参考文章

Druid架构设计思想详解

Druid.io系列(四):索引过程分析

Druid 实时数据分析存储系统

官方文档实时索引过程

Druid原理分析之“流”任务数据流转过程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值