持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情
Druid DataSource
Druid中的数据存储在被称为datasource中,类似RDMS中的table!
编辑
每个datasource按照时间划分。每个时间范围称为一个chunk(一般都是以天分区,则一个chunk为一天)!!! //也可以按其他属性划分
在chunk中数据被分为一个或多个segment,每个segment都是一个单独的文件,通常包含几百万行数据
注:这些segment是按照时间组织成的chunk,所以在按照时间查询数据时,效率非常高。
数据分区:
任何分布式存储/计算系统,都需要对数据进行合理的分区,从而实现存储和计算的均衡,以及数据并行化。
而Druid本身处理的是事件数据,每条数据都会带有一个时间戳,所以很自然的就可以使用时间进行分区。
为什么一个chunk中的数据包含多个segment!!!????原因就是二级分区
二级分区:
很可能每个chunk的数据量是不均衡的,而Duid为了解决这种问题,提供了“二级分区”,每一个二级分区称为一个Shard(分片)
其实chunk、datasource都是抽象的,实际的就是每个分区就是一个Shard,每个Shard只包含一个Segment!!!,因为Segment是Shard持久化的结果
Druid目前支持两种Shard策略:
Hash(基于维值的Hash)
Range(基于某个维度的取值范围)
譬如:
2000-01-01,2000-01-02中的每一个分区都是一个Shard
2000-01-02的数据量比较多,所以有两个Shard,分为partition0、partition1。每个分区都是一个Shard
Shard经过持久化之后就称为了Segment,Segment是数据存储、复制、均衡(Historical的负载均衡)和计算的基本单元了。
Segment具有不可变性,一个Segment一旦创建完成后(MiddleManager节点发布后)就无法被修改,
只能通过生成一个新的Segment来代替旧版本的Segment。
Segment内部存储结构:
Segment内部采用列式存储
//并不是说每列都是一个独立的文件,而是说每列有独立的数据结构,所有列都会存储在一个文件中
Segment中的数据类型主要分为三种:
时间戳
维度列
指标列
对于时间戳列和指标列,实际存储是一个数组
对于维度列不会像指标列和时间戳这么简单,因为它需要支持filter和group by:
所以Druid使用了字典编码(Dictionary Encoding)和位图索引(Bitmap Index)来存储每个维度列。每个维度列需要三个数据结构:
1、需要一个字典数据结构,将维值(维度列值都会被认为是字符串类型)映射成一个整数ID。
2、使用上面的字典编码,将该列所有维值放在一个列表中。
3、对于列中不同的值,使用bitmap数据结构标识哪些行包含这些值。 //位图索引,这个需要记住
注:使用Bitmap位图索引可以执行快速过滤操作(找到符合条件的行号,以减少读取的数据量)
Druid针对维度列之所以使用这三个数据结构,是因为:
使用字典将字符串映射成整数ID,可以紧凑的表示结构2和结构3中的值。
使用Bitmap位图索引可以执行快速过滤操作(找到符合条件的行号,以减少读取的数据量),因为Bitmap可以快速执行AND和OR操作。
对于group by和TopN操作需要使用结构2中的列值列表
因为是一个稀疏矩阵,所以比较好压缩!!
Druid而且运用了Roaring Bitmap能够对压缩后的位图直接进行布尔运算,可以大大提高查询效率和存储效率(不需要解压缩)
Segment命名:
如果一个Datasource下有几百万个Segment文件,我们又如何快速找出我们所需要的文件呢?答案就是通过文件名称快速索引查找。
Segment的命名包含四部分:
数据源(Datasource)、时间间隔(包含开始时间和结束时间两部分)、版本号和分区(Segment有分片的情况下才会有)。
eg:wikipedia_2015-09-12T00:00:00.000Z_2015-09-13T00:00:00.000Z_2019-09-09T10:06:02.498Z
wikipedia: Datasource名称
开始时间: 2015-09-12T00:00:00.000Z //该Segment所存储最早的数据,时间格式是ISO 8601
结束时间: 2015-09-13T00:00:00.000Z //该segment所存储最晚的数据,时间格式是ISO 8601
版本号: 2019-09-09T10:06:02.498Z //此Segment的启动时间,因为Druid支持批量覆盖操作,
//当批量摄入与之前相同数据源、相同时间间隔数据时,数据就会被覆盖,这时候版本号就会被更新
分片号: 从0开始,如果分区号为0,可以省略 //分区的表现其实就是分目录
注:单机形式运行Druid,这样Druid生成的Segment文件都在${DRUID_HOME}/var/druid/segments 目录下
注:为了保证Druid的查询效率,每个Segment文件的大小建议在300MB~700MB之间
注:版本号的意义:
在druid,如果您所做的只是追加数据,那么每个时间chunk只会有一个版本。
但是当您覆盖数据时,因为druid通过首先加载新数据(但不允许查询)来处理这个问题,一旦新数据全部加载,
切换所有新查询以使用这些新数据。然后它在几分钟后掉落旧段!!!
存储聚合:
无论是实时数据消费还是批量数据处理,Druid在基于DataSource机构存储数据时即可选择对任意的指标列进行聚合操作
1、基于维度列:相同的维度列数据会进行聚合
2、基于时间段:某一时间段的所有行会进行聚合,时间段可以通过queryGranularity参数指定
聚合:提前做好sum,count等操作
Segment生命周期:
在元数据存储中!每个Segment都会有一个used字段,标记该段是否能用于查询
is_Published:
当Segment构建完毕,就将元数据存储在元数据存储区中,此Segment为发布状态
is_available:
如果Segment当前可用于查询(实时任务或历史进程),则为true。
is_realtime:
如果是由实时任务产生的,那么会为true,但是一段时间之后,也会变为false
is_overshadowed:
标记该段是否已被其他段覆盖!处于此状态的段很快就会将其used标志自动设置为false。