点击上方蓝色字体,选择“设为星标”

回复”资源“获取更多惊喜

大数据技术与架构

点击右侧关注,大数据开发领域最强公众号!

大数据真好玩

点击右侧关注,大数据真好玩!

Apache Druid是一款优秀的OLAP引擎,众所周知数据存储格式对一款存储系统来说是最核心的组件,Druid的数据格式是自定义的,以此保证了在海量数据下的亚秒级查询。本文深入分析Druid V1版本数据存储格式,包括索引结构和数据在磁盘中的存储方式。在阅读本文之前希望您对Druid和数据存储有简单了解。

Druid的存储方式是列式的,每个列为一个逻辑文件,列与列之间的数据格式是相对独立的。与传统OLAP系统一样,Druid的列分为维度与度量两种,其中维度列因为需要被检检索,所以设计了索引,维度列的数据格式也是Druid数据结构的核心;相对的度量列只需要存储行值就可以。为了方便阐述数据格式,本文以一个广告效果分析作为例子进行分析,图1中是样例数据,请一定注意它是聚合后的数据,而不是原始数据。

维度数据结构

上文提到维度是Druid存储结构的核心,并且各个维度是相对独立存储的,所以我们可以通过分析单个维度的数据结构,来窥探Druid的存储结构。图2展示了“city”维度和两个度量的逻辑存储结构,整体上Druid维度的索引包含三部分:字典、编码后的维度值、倒排索引,接下来详细分析这三部分。

字典

字典是将列的所有值去重,然后按照字典顺序排序的值组成的数组,虽然字典中只存储了排序后的维度值,但是它还隐含了另一个信息,那就是每个维度值的编码值,编码值就等于数组的下标。字典的设计目的有两个:一是维度值可以使用编码后的整数表示,而不是实际的值,编码值一般可以节约存储空间;二是编码后的整数是定长的,磁盘中定长存储可以省去定位单个值的offset length等索引信息的开销,最终还是能节省存储空间。

图3展示了Druid字典的逻辑结构和物理结构,Druid字典采用了线性的数组结构。因为字典中的值是不定长的,所以物理结构中有一段index部分,其中记录了每个值的offset;data部分每个值的头部记录了该值的长度。这样的设计才能定位到任意一个行的值。

编码后的维度值

Druid是一个预聚合的方案,但是其聚合不是按照一个维度的group-by聚合,而是按照所有维度的group-by聚合,对于图1中的数据已经是按照聚合过了。可以看出对于单一维度而言,编码过后的维度值依然可能重复,所以每个维度的行信息不能用字典代替,而需要额外存储。

编码后的维度值都是一个个的整数。为了保证单一值在磁盘中能快速定位,在整个维度范围内这些整数需要是定长的,因为定长元素组成的数组可以通过计算直接定位到某一个元素。同时为了节约存储空间这些整数不一定需要用4个字节表示,它的长度取决于该维度在单一数据文件内的唯一值的数量,Druid采用了采用了变长整数编码的方式,具体如下:

1 – 2^8-1 => 1 byte
2^8 - 2^16-1 => 2 bytes
2^16 - 2^24-1 => 3 bytes
2^24 - 2^32-1 => 4 bytes
2^32 - 2^40-1 => 5 bytes
...

以图1中“city”维度为例,它包含唯一值3个,所以每个值用1个字节表示。

图4展示了编码后维度值的逻辑结构和物理结构,在逻辑上整个维度是一个线性的结构,但是在物理存储上数据结构中包含了offset索引和元素length部分,这很明显是存储非定长数据的。原来Druid将整个线性结构首先划分成了一个个分组,每个分组大小不超过64KB,而分组又进行了压缩,压缩后的分组已经是非定长的了,所以站在整个数据结构的角度,需要按照非定长数据的格式进行存储。

将整个整数数组进行分组压缩的设计思路,其背后的考量点主要是:一是对于磁盘存储压缩是有必要的,因为能减小空间占用和传输消耗;二是分组也是有必要的,因为绝大多数读取数据的场景不会涉及到所有的分组,而是部分分组,分组后一次查询只涉及到了少数分组,对于查询速度的提升有极大帮助。

倒排索引

最后是倒排索引部分,对于字典中的每个元素,Druid都会生成一个Bitmap,其中1表示该bit下标对应的行的值是对应字典元素的值,反之不是。

Bitmap数据是基于聚合后的数据的,所以它的长度和原始数据的行数是没有关系的。从图5中“Beijing”对应的Bitmap可以看出,它基于图1中的聚合后的数据,而不是原始数据,所以Bitmap的长度是4。

Druid的反向索引采用的是Bitmap的方案,因为字典中每个元素对应的Bitmap的长度都是一样的,所以物理存储上可以采用定长的方式?其实不是的,出于节省存储空间的考虑,Druid将每个Bitmap进行了压缩,一般Bitmap数据结构的压缩比例是比较大的,所以压缩的是有必要的。因为压缩后数据长度不相同了,所以存储上需要按照非定长数据进行存储。

数组

Druid是支持数组数据类型维度的,对于数组数据类型Druid如何存储呢?整体上数组的存储方式还是字典、编码后的维度值、倒排索引三个部分。其中字典和倒排索引部分是跟单值类型的维度的存储方式没有任何区别。

但是在编码后的维度值部分是有区别的,对于单值维度这部分的逻辑结构是一个线性列表(这里暂时不考虑分组),但是对于数组类型的维度,它其实是一个二层的层次结构,外层是一个非定长的线性列表,线性列表的每个元素也就是内层,是一个定长的线性列表。对于整个数据结构来说,在物理结构上依然可以进行分组和压缩。

存储结构小结

对于物理结构来说其元素是否定长,对其存储方式起到决定作用,图6总结了定长和非定长的存储模式,请注意这里没有考虑分组和压缩。

Druid对线性非定长存储结构有这大量的应用,它遵循图6的总结,只是在元数据部分稍有不同,现总结如下:

version:占用 1byte
allowReverseLookup:1byte ,是否允许反向查找,指根据Value反查index, 用于Dictionary字典查找
numBytesUsed :4bytes ,所占字节数
numElements:4bytes,元素的总数量

如何使用

最后简单分析下Druid在查询中如何使用到以上数据结构,为了聚焦问题,假设查询只命中了一个数据文件,这样可以忽略多个数据文件的结果合并等问题。我们以下面简单查询为例:

select city, sum(click_cnt) from table_t where category=0 or category=1 group by city

图8展示了查询流程,其中第1.6步用到了字典结构,第3步用到了倒排索引数据结构,第4部用到了编码后的维度值数据结构。

数据湖VS数据仓库?湖仓一体了解一下

Kylin、Druid、ClickHouse核心技术对比

万字长文,超全Spark性能优化总结

代达罗斯之殇-大数据领域小文件问题解决攻略

大数据可视化从未如此简单 - Apache Zepplien全面介绍

Kylin 大数据下的OLAP解决方案和行业典型应用

版权声明:

本文为大数据技术与架构整理,原作者独家授权。未经原作者允许转载追究侵权责任。

微信公众号|import_bigdata

编辑| 《大数据技术与架构》

文章链接| http://www.jackywoo.cn/data-structure-in-druid/

欢迎点赞+收藏+转发朋友圈素质三连

文章不错?点个【在看】吧! ????

大话 Druid 存储结构相关推荐

  1. SQL SERVER大话存储结构(2)

    阅读目录(Content) 1 行记录如何存储 1.1 堆表 1.2 聚集索引表格 2 非聚集索引结构 3 非聚集索引键值内容 3.1 堆表上的非聚集索引 3.2 聚集索引表(唯一)的非聚集索引 3. ...

  2. 大话数据结构 -07-1 图的定义、抽象数据类型与存储结构

    1. 定义 在图结构中,结点之间的关系可以是任意的,图中任意两个数据元素之间都可能相关. 2. 与线性表.树结构的区别 3. 相关定义 (1)无向.有向 与图边或者弧相关的数字叫做权(Weight), ...

  3. 《大话数据结构》----第三章---线性表链式存储结构

    目录 一.为啥要单独说线性表的链式存储结构? 二.这些链式存储结构分别是什么样的? 2.1 单链结构是怎么样的? 2.2 静态链表又是怎么定义的呢? 2.3循环链表是如何定义的? 2.4双向链表是为什 ...

  4. 《大话数据结构》5一文学会数据结构中的静态链表存储结构(概念,实例,代码)

    静态链表 1.静态链表基本介绍 (1)静态链表:用数组来代替指针,来描述单链表.我们把用数组描述下标的链表叫做静态链表.也叫游标实现法. (2)首先让数组的元素是两个数据域组成,data 和cur.也 ...

  5. 链表list(链式存储结构实现)_5 线性表的链式存储结构

    系列文章参考资料为<大话数据结构>,源码为个人私有,未经允许不得转载 线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,可以使连续的,也可以不连续,也就意味这些元素可以 ...

  6. 链表list(链式存储结构实现)_数据结构知否知否系列之 — 线性表的顺序与链式存储篇(8000 多字长文)...

    从不浪费时间的人,没有工夫抱怨时间不够. -- 杰弗逊 线性表是由 n 个数据元素组成的有限序列,也是最基本.最简单.最常用的一种数据结构. 作者简介:五月君,Nodejs Developer,热爱技 ...

  7. 线性表之链式存储结构_单链表相关算法

    在存储结构上,不需要连续的存储空间,需要上一个结点的指针域 指向下一个结点即可,找到一个结点就可以找到下一个结点. 学习教材是大话数据结构,加上自己的一些个人理解.这个算法 有点绕,需要对指针 相关内 ...

  8. 图的常见存储结构及各自的优缺点

    以下说法均建立在简单图上,即无环无重复边的图. 本文将介绍图的常见存储结构及各自的优缺点 邻接矩阵 邻接表 十字链表 邻接多重表 边集数组 邻接矩阵 用两个数组来表示图:一个一维数组存储图中顶点信息, ...

  9. 线性表-链式存储结构

    3.6 线性表的链式存储结构 3.6.1 顺序存储结构不足的解决办法 前面我们讲的线性表的顺序存储结构.它是有缺点的,最大的缺点就是插入和删除时需要移动大量元素,这显然就需要耗费时间.能不能想办法解决 ...

最新文章

  1. Widgets 整理
  2. 关于深度学习的小知识点
  3. 原生js的attachEvent和addEventListener解决window.onload在一个页面只能执行一次的问题
  4. C++入门之常量与变量
  5. Oracle数据库DECODE函数的使用.
  6. 利用Underscore求数组的交集、并集和差集
  7. C语言(CED)gameboy接馅饼问题
  8. Sharepoint 站点模板和站点定义、定制站点定义
  9. 记 2022年11月5日 信息安全工程师考试
  10. 高级前端面试100问(必会)
  11. 奇虎360历届笔试面试题汇总
  12. dbf解析_JAVA解析DBF文件方案.pdf
  13. pyecharts根据经纬度画动态散点地图
  14. Linux那些事儿 之 我是PCI(1)PCI,我们来了
  15. android sdk 固态硬盘,使用TVM在android中进行Mobilenet SSD部署
  16. win10系统Onedrive登录输入邮箱后界面空白的解决方法
  17. Windows编程 第四回 Windows程序的生与死(下)
  18. 今天把积累几年的49个实用工具分享出来,涉及各个方面的工具,进来看一看咯。
  19. pyltp的初始化报错:segmentor = Segmentor() # 初始化实例TypeError: __init__(): incompatible constructor argument
  20. excel文件下载下来损坏 js_js读取本地excel文件出现问题,这是咋回事

热门文章

  1. 面经九2023.2.3上午笔试和群面
  2. 生日礼物(winter camp F)
  3. Java基础之代理模式
  4. Git - 如何checkout一个tag
  5. 计算机改变我们生活英语作文,How did computer change our live?(计算机如何改变我们的生活?初中英语作文)...
  6. Docker配置Daocloud加速器
  7. 什么是防关联浏览器?
  8. 介绍计算机硬件的英语作文带翻译,自我介绍作文之英语作文自我介绍带翻译(35页)-原创力文档...
  9. ubuntu18.04环境下配置opencv c++环境(make实现cpp编译)
  10. Win10任务栏透明