@Wishes
2021-04-02T02:27:57.000000Z
字数 1647
阅读 269
Druid是一款面向大数据的实时查询引擎,采用列式存储可以很好的提升查询的性能。
影响查询的性能的一个重要的因素就是schema的设计,druid有三种列,如下图所示:
Timestamp列是每行数据必须要带的时间列,metric列是指标列,用于在查询的时候需要做聚合操作(如sum()函数),所以纯数值类型比较合适。
时间列和指标列的存储相对而言是比较简单的,只需要将每列的内容用LZ4压缩算法压缩然后存起来就可以了,在查询的时候,将对应列的内容进行解压缩,然后做聚合操作。
维度列(Dimensions columns)相比上面的两种列会复杂一些,因为维度列要支持过滤和分组。
维度列有4中类型:string, Long, Double, Float。
维度列和指标列的数字类型存储完全一致,只简单存储了列的值并压缩,官方文档中也说明了数字类型是没有索引的,所以维度列如果需要过滤,最好就不要用数值类型。
A dictionary that maps values (which are always treated as strings) to integer IDs,
A list of the column’s values, encoded using the dictionary in 1, and
For each distinct value in the column, a bitmap that indicates which rows contain that value.
string是druid最核心的类型,为了支持大量数据下的过滤操作,string类型用了倒排索引。所以string类型在存储的时候,需要存索引和值两部分数据。
假设数据中,某一列的值如下:
R0 = "Justin Bieber"
R1 = "Justin Bieber"
R2 = "Ke$ha"
R3 = "Ke$ha"
我们就能得到value -> rowId 列表
的倒排索引结构:
"Justin Bieber": [0, 1]
"Ke$ha": [2, 3]
这一列的值存储结构:
[Justin Bieber, Justin Bieber, Ke$ha, Ke$ha]
想象一下,如果数据的行数不是4行,而是4亿行,那么倒排索引的rowId列表和列值会变得很大,druid为了优化这种情况,分别用了:
1. 列值的存储,增加了一个对列值的编码字典如下:
Justin Bieber=0
Ke$ha=1
这样列值中就不需要存原始的字符串,而变成了:
[0, 0, 1, 1]
列值的存储空间瞬间减小。
2. rowId列表用bitmap代替整数列表, rowId列表变成了:
"Justin Bieber": [1, 1, 0, 0]
"Ke$ha": [0, 0, 1, 1]
druid 默认的bitmap是roaring bitmap, rowId列表如果比较稀疏会被很好压缩, 如果不稀疏,那会比直接存整数节省很多内存。
还有一种特殊的情况就是多值列,假设数据中,某一列的值如下:
R0 = "Justin Bieber"
R1 = "Justin Bieber"
R2 = ["Ke$ha", "Justin Bieber"]
R3 = "Ke$ha"
根据上述的内容,列值的编码字典保持不变,倒排索引结构如下:
"Justin Bieber": [1, 1, 1, 0]
"Ke$ha": [0, 0, 1, 1]
这一列的值存储结构如下:
[0, 0, [0, 1], 1]
所以,string类型的数据要存3部分内容:
1. 列值和对应的编码的字典
2. 转换成编码后的列值
3. 倒排列表
string类型的意义就是加速查询,假设查询Justin Bieber
所在的行,直接从倒排索引中就能找到对应的bitmap。
但是仔细思考的话,会发现string类型的列值存的是列值的编码,而不是列值本身,这样虽然节省了存储空间,但是如果需要返回这一列的值,性能就会有损耗,因为需要把列值的编码还原成列值本身。
就整体而言,在schema设计的时候,如果某一列涉及到过滤操作,最好将该列设计成维度列中的字符串类型是很必要的。