Druid按时间分区以后,将索引信息存储在segment文件里面。在基础的配置安装里,每个segment对应一个时间区间(时间区间定义参考granularitySpec中的 segmentGranularity参数:url链接)。为了在高负载的情况下提供良好的响应性能,强烈推荐segment的大小限制在推荐值区间(300mb-700mb),如果你的segment文件大小超过这个区间,可以考虑改变时间区间的粒度,或者对数据进行分片并微调PartitionSpec中的targetPartitionSize设置(建议在数据量超过500万行以后再开启这项设置)。更多分片信息可以参考Batch ingestion中的“Partitioning Specification”部分内容。 Segment文件核心数据结构下面介绍一个segment文件的内部结构,其本质是一种列式存储:每列的数据被独立存储在segment结构的不同位置。通过独立存储每列数据,Druid在执行查询请求时可以只访问相关列的数据,因此可以大幅提高性能。Druid的列有三种基本类型:Timestamp类型,dimension类型和metric类型,如下图所示: Timestamp列和Metric列比较简单,他们在实现中被存储为通过lz4压缩的整型或浮点数组。当一个查询需要访问某列数据时,只需要解压缩这些列,然后读取出这些列的数据,然后执行预定义的聚合操作。对于查询过程中不需要的列,Druid会直接跳过对这些列数据的访问。 Dimension列由于要实现filter和group-by操作,它的实现有些不同,dimension列包行下列三种数据结构:
为什么我们需要这三种数据结构呢?通过字典将字符串映射成数字id,数字id通常比字符串更小,因此可以被更紧凑的存储。第三点中的bitmap,通常被称为倒排索引,用于支持快速的过滤操作(bitmap对AND和OR操作的速度非常快)。最后,第二点中的值列表将用于支持group by和topN查询,而普通的查询只需要根据filter出来的行去来聚合相应的metirc就可以了,而不需要访问上述2中的值列表。 下面以上面表格中的page列数据为例,来演示一下这三种数据结构,如下所示:
注意bitmap跟其他两种数据结构不同,其他两种数据结构都是跟随数据量的增长而线性增长的(最差情况下),而bitmap的大小=数据的总行数*列中值的种类数(也就是字典的size)。这就意味着如果值得的种类很多,那么bitmap中为1的数量将会非常稀疏,这种情况下bitmap有可能被大幅压缩。Druid针对这种情况,采用了特殊的压缩算法,比如roaring bitmap压缩算法。 多值(multi-value)列如果datasource使用了多值列,那么segment里面的数据结构就稍微有些不同。让我们对上面例子中的数据稍微做一下修改,来演示多之列的情况。假设上述表格中的第二行的page列,同时被标注为Ke$ha和Justin Bieber,这种情况下,上面的三种数据结构如下图所示:
请注意这个数据跟上面数据在列值数据的第二行和bitmap的第二位与原来数据的区别。如果某一行在特定列上有多个值,那么在列值表里面,该位置对应的是一个数组;在bitmap的对应行位置会有多个非0值。 命名规范Segment的标识通常由datasource的名字,时间的起始和结束时间(ISO8601格式),以及一个版本号组成。如果数据在时间区间内还被分片了,那么名字也对应包含一个分片号。 一个segment的名字示例如下:datasource_intervalStart_intervalEnd_version_partitionNum Segment组件Segment通常由多个文件组成,如下所示:
__time列时druid中的一种特殊列,用来表示segment的时间列。从代码的角度来看,这一列应该跟其他列没有本质区别,但是目前为止,druid里还是有一些_time列的特殊逻辑。 在代码中,segment有一个内部的版本号,目前的版本号是v9 列的格式每一列被保存为两部分:
ColumnDescriptor是必须的,它被序列化为json格式,因此可以很方便的在不影响老代码的情况下,在里面添加新功能的meta信息。它包含了列所必要的一些元信息。 Segment的分区(Sharding)对于有些datasource来说,同一时间区间之内可能存在多个segment。这些segment构成了该时间区间的一个block。根据shardspec配置有两种类型,其中之一要求druid只能在block里面的segment都ready的情况下才能提供查询。也就是说,如果有下面3个segment:
所有的这三个segment必须都被加载以后才能提供查询。 但是当使用线性分片配置时,druid并不要求所有的segment都ready时才能提供查询。比如,在使用线性分片配置时,当你的实时数据导入模块创建了3个segment,如果只有两个segment被加载,druid将只返回在这两个segment上的查询结果。 |
|