博客 Druid架构以及数据存储

Druid架构以及数据存储

   数栈君   发表于 2023-12-01 09:47  107  0

Druid 的目标是提供一个能够在大数据集上做实时数据摄入与查询的平台,然而对于大多数系统而言,提供数据的快速摄入与提供快速查询是难以同时实现的两个指标。例如对于普通的RDBMS,如果想要获取更快的查询速度,就会因为创建索引而牺牲掉写入的速度,如果想要更快的写入速度,则索引的创建就会受到限制。Druid通常是基于时序的事实事件,事实发生后进入Druid,外部系统就可以对该事实进行查询。
Druid系统架构

Druid是一组系统,按照职责分成不同的角色。目前存在五种节点类型:

Historical: 历史节点的职责主要是对历史的数据进行存储和查询,历史节点从Deep Storage下载Segment,然后响应Broker对于Segment的查询将查询结果返回给Broker节点,它们通过Zookeeper来声明自己存储的节点,同时也通过zookeeper来监听加载或删除Segment的信号。
Coordinator:协调节点监测一组历史节点来保证数据的可用和冗余。协调节点读取元数据存储来确定哪些Segment需要load到集群中,通过zk来感知Historical节点的存在,通过在Zookeeper上创建entry来和Historical节点通信来告诉他们加载或者删除Segment。
Broker:节点接收外部客户端的查询,并且将查询路由到历史节点和实时节点。当Broker收到返回的结果的时候,它将结果merge起来然后返回给调用者。Broker通过ZooKeeper来感知实时节点和历史节点的存在。
MiddleManager:负责接收数据。
Overlord:控制数据提取工作负载的分配。
Router:可选的进程,可以将请求路由到Broker,Coordinator和Overlords。
Indexing Service: 索引服务是一些worker用来从实时获取数据或者批量插入数据。
Realtime:获取实时数据

Druid还依赖于外部的三个组件:

ZooKeeper :用于内部服务发现、协调和领导选举。
Metadata Storage :元数据存储实例,如:MySQL
Deep Storage :每个druid服务器都可以访问共享文件存储。通常是一个分布式对象存储,如S3或HDFS。
http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/5ed9e189faa197949ffafa04cd47f295..png

数据流动图
http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/48bc3d4491765926521e120477c932e2..png

数据存储

LSM-Tree适合哪种写操作要远远大于DELETE/UPDATE/QUERY的应用场景,这正好符合Druid的使用场景,所以Druid的文件组织方式与LSM-Tree类似。
Datasources

Druid将数据组织成Read-Optimized的结构,而这也是Druid能够支持交互式查询的关键。Druid中的数据存储在被称为datasource中,类似RDMS中的table。每个datasource按照时间划分,如果你有需求也可以进一步按其它属性划分。每个时间范围称为一个chunk(比如你按天分区,则一个chunk为一天)。在chunk中数据由被分为一个或多个segment(segment是数据实际存储结构,Datasource、Chunk只是一个逻辑概念),每个segment都是一个单独的文件,通常包含几百万行数据,这些segment是按照时间组织成的chunk,所以在按照时间查询数据时,效率非常高。

Datasource包含下面三个重要的概念:

时间列(Timestamp):每行数据的时间值,默认使用UTC时间格式,保存到毫秒级别,本列是数据聚合以及范围查询的重要指标
维度列(Dimension):标识数据行的列,可以是一列,也可以是多列
指标列(Metric):用来做计算或是统计的列,可以是一列,也可以是多列

相对于其他数据库,Druid Datasource最大的特点是在输入存储时,就可以对数据进行聚合操作,该特性不仅可以节省存储的空间,而且可以提高聚合查询的效率。
http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/810e18038cd5c59fbab8e40c4a07f1e8..png

数据分区

任何分布式存储/计算系统,都需要对数据进行合理的分区,从而实现存储和计算的均衡,以及数据并行化。而Druid本身处理的是事件数据,每条数据都会带有一个时间戳,所以很自然的就可以使用时间进行分区。比如上图,我们指定了分区粒度为为天,那么每天的数据都会被单独存储和查询。

使用时间分区我们很容易会想到一个问题,就是很可能每个时间段的数据量是不均衡的(想一想我们的业务场景),而Duid为了解决这种问题,提供了“二级分区”,每一个二级分区称为一个Shard(这才是物理分区)。通过设置每个Shard的所能存储的目标值和Shard策略,来完成shard的分区。Druid目前支持两种Shard策略:Hash(基于维值的Hash)和Range(基于某个维度的取值范围)。上图中,2000-01-01和2000-01-03的每个分区都是一个Shard,由于2000-01-02的数据量比较多,所以有两个Shard。
Segment

Shard经过持久化之后就称为了Segment,Segment是数据存储、复制、均衡(Historical的负载均衡)和计算的基本单元了。Segment具有不可变性,一个Segment一旦创建完成后(MiddleManager节点发布后)就无法被修改,只能通过生成一个新的Segment来代替旧版本的Segment。

Segment为Druid中数据的物理存储格式,Segment通过以下特性来支撑Druid的高性能:

数据的横向切割:横向切割主要只指站在时间范围的角度,将不同时间段的数据存储在不同的Segment文件中(时间范围可以通过segmentGranularity进行设置),查询时只需要根据时间条件遍历对应的Segment文件即可。
数据的纵向切割:面向列进行进行数据压缩
使用BitMap等技术对数据访问进行优化

Segment内部存储结构
http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/35ab5f87e3bbcd6bdac54d40eb0debd8..png

因为Druid采用列式存储,所以每列数据都是在独立的结构中存储(并不是独立的文件,是独立的数据结构,因为所有列都会存储在一个文件中)。Segment中的数据类型主要分为三种:时间戳、维度列和指标列。

对于时间戳列和指标列,实际存储是一个数组,Druid采用LZ4压缩每列的整数或浮点数。当收到查询请求后,会拉出所需的行数据(对于不需要的列不会拉出来),并且对其进行解压缩。解压缩完之后,在应用具体的聚合函数。

对于维度列不会像指标列和时间戳这么简单,因为它需要支持filter和group by,所以Druid使用了字典编码(Dictionary Encoding)和位图索引(Bitmap Index)来存储每个维度列。每个维度列需要三个数据结构:

需要一个字典数据结构,将维值(维度列值都会被认为是字符串类型)映射成一个整数ID。
使用上面的字典编码,将该列所有维值放在一个列表中。
对于列中不同的值,使用bitmap数据结构标识哪些行包含这些值。

Druid针对维度列之所以使用这三个数据结构,是因为:

使用字典将字符串映射成整数ID,可以紧凑的表示结构2和结构3中的值。
使用Bitmap位图索引可以执行快速过滤操作(找到符合条件的行号,以减少读取的数据量),因为Bitmap可以快速执行AND和OR操作。
对于group by和TopN操作需要使用结构2中的列值列表。

"Page"维度列为例,可以具体看下Druid是如何使用这三种数据结构存储维度列:

1. 使用字典将列值映射为整数
{
"Justin Bieher":0,
"ke$ha":1
}
2. 使用1中的编码,将列值放到一个列表中
[0,0,1,1]
3. 使用bitmap来标识不同列值
value = 0: [1,1,0,0] //1代表该行含有该值,0标识不含有
value = 1: [0,0,1,1]

查询流程

查询首先进入Broker,Broker将识别哪些段具有可能与该查询相关的数据。段列表始终按时间进行修剪,也可能会被其他属性修剪,具体取决于数据源的分区方式。然后,Broker将识别哪些Historicals和MiddleManagers正在为这些段提供服务,并向每个进程发送重写的子查询。 Historical / MiddleManager进程将接受查询,处理它们并返回结果。Broker接收结果并将它们合并在一起以获得最终答案,并将其返回给原始调用者。

Broker修剪是Druid限制每个查询必须扫描的数据量的重要方式,但这不是唯一的方法。对于比Broker可用于修剪更细粒度的过滤器,每个段内的索引结构允许Druid在查看任何数据行之前确定哪些(如果有)行匹配过滤器集。一旦Druid知道哪些行与特定查询匹配,它只访问该查询所需的特定列。在这些列中,Druid可以在行之间跳过,避免读取与查询过滤器不匹配的数据。

所以Druid使用三种不同的技术来最大化查询性能:

修剪为每个查询访问哪些段。
在每个段内,使用索引来标识必须访问的行。
在每个段内,仅读取与特定查询相关的特定行和列。



参考:

https://druid.apache.org/docs/latest/design/

Druid高效架构

Druid(一)——Druid架构概览

Druid 架构详解

Druid.io
————————————————
版权声明:本文为CSDN博主「huaishu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/huaishu/article/details/96987055

免责申明:

本文系转载,版权归原作者所有,如若侵权请联系我们进行删除!

《数据治理行业实践白皮书》下载地址:https://fs80.cn/4w2atu

《数栈V6.0产品白皮书》下载地址:
https://fs80.cn/cw0iw1

想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:
https://www.dtstack.com/?src=bbs

同时,欢迎对大数据开源项目有兴趣的同学加入「袋鼠云开源框架钉钉技术群」,交流最新开源技术信息,群号码:30537511,项目地址:
https://github.com/DTStack



0条评论
社区公告
  • 大数据领域最专业的产品&技术交流社区,专注于探讨与分享大数据领域有趣又火热的信息,专业又专注的数据人园地

最新活动更多
微信扫码获取数字化转型资料
钉钉扫码加入技术交流群