目录
什么是druid
druid使用场景
Druid架构
datasources和segments
查询处理流程
外部依赖
深度存储
元数据存储
ZooKeeper
什么是druid
druid设计之处的目的是为了在大数据集上进行高效的slice-and-dice分析(类似OLAP)。druid经常会被用来作为强大可视化分析的存储模块,或者为高速的并发api提供支持。它的典型应用场景包括:
- 用户点击分析
- 网络流量分析
- 服务器度量存储
- 应用的性能度量
- 数字市场分析
- BI/OLAP
Druid的主要特色
- 列式存储
- 分布式存储
- 多并发处理
- 支持实时/批量数据导入
- 自我治愈、自我管理、易操作
- 支持原生云,较好的若错功能不易丢失数据
- 近似算法
- 在导入数据的时候同时进行汇总计算
druid使用场景
druid的主要使用场景如下:
- 插入频率很高,更新频率不高
- 大部分的查询是面向聚合操作或者报表需求的
- 数据是具有时间属性的
- 系统可能有多个表,但是每次查询主要是对一个大分布式的表进行查询
- 有的列具有非常多的数据,需要对它们进行快速统计或排序
- 你希望从kafka、hdfs或面向对象存储诸如s3等
druid不适用的场景包括:
- 希望快速的更新数据,druid支持流式数据的插入,但不支持更新
- 你在创建离线报表,对数据的实时性要求不高
- 你希望做很多的关联
Druid架构
druid是面向多进程、分布式的架构,它的设计是面向云服务而且是易操作的。每个druid进程都是单独进行配置的,这使得集群具有非常灵活的特性。这种设计使得系统具有很好的容错性,即使一个节点发生宕机也不会影响其他节点。
druid的进程主要有:
- Historical 这种重负荷进程负责处理“historical”数据的存储和查询(包括将要提交的流式数据)。这类进程会从深度存储中下载segment并且进行响应,需要注意的是它不支持写入功能。
- MiddleManager 这种进程负责将数据写入集群,它们负责从外部接收数据并且写入druid新的segment中。
- Broker 这种进程会接收外部客户端的查询并且将请求转发至Historical和MiddleManager节点,等待它们返回之后,Broker会将结果合并并且向客户端进行响应。
- Coordinator 这种进程负责监护Historical进程,它们负责分配segment以保证各Historical能够均衡地进行处理数据。
- Overload 这种进程是druid数据加载的核心控制器,它负责监护MiddleManager。Overload会将数据加载任务分配给MiddleManager,并且协助分发segment。
- Router 它负责对Broker、Coordinator和Overload进行路由解析,应用客户端也可以直接和Broker、Coordinator与Overload进行交互,所以它是可选的。
Druid的各种进程可以分开部署,也可以一起部署,其中一种通用的部署方式是:
- “Data”服务器负责运行Historical和MiddleManager进程
- “Query”服务器负责运行Broker和Router(可选的)进程
- “Master”服务器上跑Coordinator进程和Overload进程
除了上面描述的这些进程之外,Druid也需要三种外部的依赖。它们会使得整个架构更加合理,描述如下:
- Deep Storage 这是一种共享的文件存储,主要是一些分布式对象存储诸如s3或hdfs,也可能是网络文件系统。
- Metadata store 共享的元数据存储,主要是一些传统关系型数据库如PostgreSql或者Mysql
- Zookeeper 用于内部服务寻址,协调和主节点的选举等
Druid的架构之所以这样设计,是因为它会使得druid集群更加容易操作。譬如将深度存储和元数据存储同集群其他节点分开,这将使得集群具有较好的容错功能,即使druid集群的所有服务器都挂了,你依然可以使用深度存储和元数据存储将集群进行启动。
下图描述了整个druid架构的查询和数据流程
datasources和segments
Druid的数据存储在“datasources”中,这些和传统的RDBMS数据表非常类似。每个datasource都通过时间或者其他属性进行分区。每个时间段范围的数据称为“chunk”。在一个chunk中,数据又会分区为若干segments,每个segment都是一个单独的文件,通常情况下是由几百万条数据组成。我们知道segment是位于time chunk中的,我们可以理解为时间线上的数据,如下:
每个数据源可能只由几个segment,也可能会有数百万的segment组成。 每个segment最初是由MiddleManager创建,这个时候它是可变的,并且还没有提交到深度存储当中。segment处理进程通过以下步骤来生成一个压缩过且支持快查的文件:
- 列式存储
- 适用位图技术进行索引
- 使用若干算法进行压缩
这些算法包括:
- 针对字符串列使用字典编码进行最小化存储
- 位图压缩技术
- 所有列的类型感知压缩
segment块会定时地被提交和推送至深度存储当中。一旦存入,这些数据就不可更改,其控制权也由MiddleManager转移到Historical进程。同时,一条关于segment的记录也会被存储到metadata 存储中。这条记录是关于segment自我描述块,这些数据包括segment的结构,大小和它在深度存储中的位置。这些记录可以帮助Coordinator知道集群中数据的位置。
查询处理流程
查询请求会首先进入到Broker,broker会识别哪些segment可能和这次请求有关。segment列表通常都是通过时间或者其他属性进行过滤。broker会识别出哪些Historical和MiddleManager在提供相关的segment,之后它会将请求发送给这些进程进行处理。Historical和Middlemanager会处理请求,之后返回结果。Broker会接收数据,并将它们进行汇总并且最终返回给最初的调用者。
Broker筛选功能是非常重要的,它会帮助识别需要扫描哪些数据,但又不仅仅如此。为了进行更细粒度的筛选,每个segment中的索引结构帮助druid识别哪些行和查询的请求有关。一旦Druid识别出有用的行,它就仅仅存取和该请求有关的特定列。在这些列中,druid可以跳跃无用的行去找到有用的数据。
总结以下,druid通过三种技术来提升效率:
- 筛选有用的segment
- 在segment中,使用索引去确定哪些行必须读取
- 在segment中,仅仅读取相关的行和列
外部依赖
深度存储
Druid使用深度存储只是作为数据的备份,或者是作为在后端进程之间传输数据的方式。为了响应一次查询,Historical进程不会从深度存储中获取数据,而是从存储在本地磁盘中预处理过的segment。这意味着Druid从来也不需要在查询的时候从深度存储中存取数据,着可以极大地降低延迟。这也意味着无论对于深度存储,或者是Historical节点的数据你必须又足够的磁盘空间。
元数据存储
元数据存储系统中容纳了各种元数据,比如segment有效性信息、任务信息等。
ZooKeeper
Druid使用Zookeeper来管理当前集群的状态。