实时OLAP引擎Druid.io——节点类型

  |   0 评论   |   2,671 浏览

整体架构

diagram-7.png

Historical nodes历史节点

Historical是处理存储和查询“历史”数据(包括系统中已经存在足够长时间以提交的任何流数据)的主要工具。

Historical进程从deep storage中下载segments并响应有关这些segments的查询。

Historical节点在Zookeeper中通告他们的在线状态和他们提供的数据,加载和删除数据块(Segment)的指令通过Zookeeper发送,指令包含了数据块(Segment)在DeepStorage中的位置,以及如何解压和处理数据块(Segment)。

在Historical节点从DeepStorage下载某个指定的数据块(Segment)之前,它首先检查本地缓存,缓存中维护了一组关于哪些数据块(Segment)已经存在于当前节点的信息。如果数据块(Segment)在本地缓存中不存在,Historical节点将会从DeepStorage中下载上述指定的数据块(Segment)。

该过程如下图所示。一旦下载执行完成,数据块(Segment)的相关信息就会在Zookeeper中通告。此时,数据块(Segment)即可被查询。

本地缓存也允许Historical节点快速更新和重启。

在初始化的时候,节点检索其自身缓存的所有数据并立即提供数据服务。

Historical节点从DeepStorage中下载不可变数据块(Segment)。数据块(Segment)必须在载入内存之后才能被查询到。


Historical节点可以支持一致性读(read consistency)因为它们仅处理不可变数据。不可变数据块也允许简单的并行化模型:Historical节点可以并行地扫描和聚合不可变数据块,而不会被阻塞。

Historical不接受写请求。

层的分组功能

在不同的层,Historical节点可以分为不同的组,在一个给定的层中,所有的节点都使用统一的配置。可以为每一层设置不同的性能和容错能力参数。

把节点分层的目的是为了能够根据数据块(Segment)的重要性,将数据设定为更高或者更低的优先级分发。举例来说,可以将拥有更多核心(core)和更大内存容量的Historical节点提升到“热数据”层(“hot”Tier)。热数据集群可以配置用以下载更经常被访问的数据。同样的,也可以用更少的强大硬件来创建冷数据集群(“cold”cluster)。冷数据集群中将紧紧含有那些不经常被访问的数据块(Segment)。

可用性

Historical节点的数据块(Segment)加载和卸载指令依赖Zookeeper。假如Zookeeper变得不可用,Historical节点就不能再提供新数据或者删除旧数据,然而,由于查询服务是基于HTTP协议的,Historical节点仍然可以基于它们现有的数据为查询请求提供响应。这意味着Zookeeper停机不影响Historical节点的现有数据可用性

Coordinator nodes协调节点

Coordinator节点主要负责Historical节点上的数据管理和分配。Coordinator节点通知Historical节点加载新数据,删除旧数据,复制数据和移动数据以均衡负载。

Druid使用多版本并发控制交换协议来管理不可变数据块(Segment),以保持视图的稳定性。如果任何不可变数据块(Segment)包含了已被新的数据块(Segment)完全废弃了的数据,旧的数据块(Segment)就会被从集群中删除。

Coordinator节点经过一个领导者选举(leader-election)过程确定一个唯一的节点来发挥协调者(Coordinator)的功能。剩余的Coordinator节点作为冗余备份。

Coordinator节点定期运行,以确定集群的当前状态。Coordinator节点通过比较集群的预期状态和当前实际运行时状态做决定。

和所有Druid节点一样,Coordinator节点通过维持一个和Zookeeper的连接来获取集群的当前信息。

Coordinator节点也维持一个和MySQL数据库的连接,数据库中包含有额外的操作参数和配置信息。存在于MySQL数据库中的其中一块关键信息,是一个包含应该由Historical节点提供的数据块(Segment)列表的数据库表(table)。该表可以被任何能创建数据块(Segment)的服务更新,比如MiddleManager节点。

MySQL数据库也包含一个规则表(rule table),该表管理着在集群中,数据块(Segment)怎样被创建?如何被销毁?以及怎样被复制。

Coordinator节点通过从元数据存储中读取元数据信息来判断哪些segments是应该加载到集群的,使用Zookeeper去判断哪些历史节点是存活的,在Zookeeper中创建任务条目告诉历史节点去加载和删除segments。

管理界面

image.png

Broker nodes代理节点

Broker代理接收客户端查询并将这些查询转发给Historicals和MiddleManager。当Brokers从这些子查询中收到结果时,它们会合并这些结果,然后将它们返回给调用者。用户通常会查询Broker,而不是直接查询Historicals或MiddleManager。

Broker节点能够从Zookeeper中发布的元数据(Metadata)中知道:哪些数据块(Segment)是可查询的?以及这些数据的位置在哪?

缓存

Broker节点维护一个LRU的失效策略缓存。上述缓存可以使用本地堆内存或者外部分布式key/value数据仓库,如Memcached。

每一次一个Broker节点收到一个查询请求,它首先将该查询映射到一系列的数据块(Segment)。某些数据块(Segment)的结果可能已经存在于缓存之中,就没必要重新计算。对于那些缓存中没有的结果,Broker节点将会转发上述查询到正确的Historical节点和MiddleManager节点。一旦Historical节点返回其计算结果,Broker节点将以每一个数据块(Segment)为基准缓存这些结果,以备后用。这一过程如下图所示。

image.png

实时数据不会被缓存,因此对实时数据的请求总是被转发到MiddleManager节点。实时数据一直在变化,所以缓存实时数据的计算结果没有可信性。

缓存也作为一个额外层次的数据可用性。万一所有的 Historical 节点都不可用,这些缓存下来的计算结果可能仍然可以响应查询

可用性

假如所有的Zookeeper都停机,数据依然可查询。如果Broker节点无法和Zookeeper通信,它们使用其最后一次获知的集群视图(view of the cluster)继续转发查询到MiddleManager和Historical节点。Broker节点假定集群的结构和停机之前相同。

Overlord 节点

Overlord监视MiddleManager进程,并且是数据摄入Druid的控制器。它们负责将提取任务分配给MiddleManager并协调segment发布。

Overlord控制台

Overlord控制台可以查看等待的任务、运行的任务、可用的worker,最近创建和结束的worker。Overlord控制台访问方式:

http://<OVERLORD_IP>:<port>/console.html

image.png

MiddleManager节点

MiddleManager将新数据摄取到集群中。负责从外部数据源读取数据并发布新的Druid segments。

MiddleManager节点封装了摄入和查询事件流的功能。经过该节点的事件被实时索引,且对查询即时可用。节点只关注一段较短时间范围内的有效事件,并且定期地将其在这段较短时间范围内收集到的一批批不可变事件,传送给集群中专门处理批量不可变事件的其他节点。

MiddleManager节点利用Zookeeper来跟集群中剩余的其它节点协调配合。所有节点在ZooKeeper中通知它们的在线状态和它们所提供的数据。

MiddleManager节点为所有将要流入的事件维护一个内存索引缓冲器(Indexbuffer)。当有事件摄入,且索引可以被直接查询的时候,这些存在于缓冲器中的索引将会增量更新。对于事件查询,Druid表现为一个存在于JVM基于堆的缓冲器当中的存储行。

为避免堆溢出的问题,MiddleManager节点将周期性地,或者当达到某些最大行数限制后,持久化其存在于内存当中的索引到硬盘。这个持久化的过程将内存缓冲器(In-memory buffer)中的数据转换成为一个列存储格式(第4部分描述)。任何一个已被持久化了的索引都是固定不变的,为了能够使其仍然能够被查询,MiddleManager节点将已持久化了的索引装载到堆外内存(off-Heapmemory)。该过程如下图所示

Real-Time节点将事件缓冲(buffer)到一个内存索引(in-memory index)当中,该索引 将会被定期被持久化到硬盘。已持久化的索引将在传递出去之前被定期合并。查询将会同 时命中内存中或者已持久化的索引

所有MiddleManager节点将周期性地执行一个用以搜索所有本地已持久化的索引的后台任务。该任务合并上述这些索引,然后创建一个不可变数据块,该数据块包含着某个时间跨度范围内的所有已被MiddleManager节点摄入的事件,我们称这样的一个数据块为一个“Segment”。

在数据传输阶段,MiddleManager节点上传上述数据块(Segment)到永久备份存储中,通常是一个分布式文件系统,如S3或HDFS。Druid称这些永久数据备份存储为“DeepStorage”

下图阐明了MiddleManager节点的一系列操作。节点在13:37开始,并且只接收当前一小时和下个小时的事件。当事件被摄入后,节点通告其正在提供一个时间区间为13:00~14:00的数据块(Segment)。每10分钟(持久化周期,可配置),节点会将其在内存中缓冲的数据持久化到硬盘,并刷新内存缓冲区。

image.png

在这个小时临近结束的时候,节点可能会收到14:00~15:00之间的事件。在这个时候,节点准备为下个小时提供数据,并创建一个新的内存索引(in-memory index)。紧接着,节点通告它也同时提供一个时间区间为14:00~15:00的数据块(Segment)。节点并没有立刻合并已持久化了的,在13:00~14:00的索引,而是等待一个可配置的窗口期(windowperiod),以保证13:00~14:00这个时间区间里延后的事件能够到达。这个窗口期使因事件传送延迟而造成的数据丢失的风险降到最低。在窗口期的末尾,节点合并所有已持久化了的13:00~14:00间的索引成为一个不可变数据块(Segment),并将其分发出去。一旦该数据块(Segment)被成功加载,并且在Druid集群中的其他地方可查,MiddleManager节点就刷新其所有已收到的13:00~14:00间的数据相关信息,同时通告它正在提供这些数据


可用性和可扩展性

MiddleManager节点是数据消费者,且需要生产者提供数据流。通常,如下图所示,为了满足数据持久化的要求,我们会在生产者和MiddleManager节点之间部署像Kafka这样的消息总线。MiddleManager节点靠从消息总线中读取事件来摄入数据。通常情况下,从事件产生到事件消费的时间大约为几百毫秒。

image.png

上图消息总线有两个目的

第一,消息总线作为流入事件的缓冲器。消息总线,如Kafka,维护位置偏移用来标识一个消费者(MiddleManager节点)在事件流中读取了多远。消费者可以以编程的方式更新这些偏移。MiddleManager节点在每次持久化它们内存缓冲器中的数据到硬盘时更新该偏移量。在失败或者恢复的场景中,如果一个节点没有丢失硬盘,它可以重新从硬盘加载所有已持久化了的所有索引,然后继续从该节点最后提交的偏移量开始读取事件。从最近提交的偏移量开始摄入事件极大地缩短了节点恢复的时间。实践中,我们发现节点在几秒钟之内就可以从失败场景中恢复。

第二,消息总线的另一个作用是,可以让多个MiddleManager节点从同一个端点读取事件。多个MiddleManager节点可以从消息总线中摄入同一批事件,从而创建事件副本。在一个节点完全不可用,而且硬盘也丢失的情况下,已复制的事件流能确保不会丢失数据。一个单一的数据摄入端点也同样允许数据流分片,从而使多个MiddleManager节点每次摄入一个数据分片。这允许额外的MiddleManager节点可以无缝加入。在实践中,这种模型已经支撑了一个最大的Druid集群用于生产环境,该集群可以消费大约500MB/s(150,000 Events/s or 2 TB/hour)的原始数据。

Router节点

Router是可选的,用于在Druid Brokers,Overlords和Coordinator之前提供统一的API网关。它们是可选的,因为您也可以直接访问Druid Brokers, Overlords, and Coordinator。

读后有收获可以支付宝请作者喝咖啡