Durid(二): 数据集及存储
druid有三种类型的数据结构: timestamp列,维度列,指标列. 时间撮和指标在底层都是int数组或long数组. 指标值是int或long,而时间撮为long. Segment文件的内部结构可以看做是列式存储. 每一列的数据都是以不同的数据结果存储. 通过列式存储,查询时只查询需要的列可以减少延迟. 因为列式存储,要保存的是某一列的所有行. 所以数组的每一个元素表示的是每一行的这一列的值. 列式存储还有一个好处是压缩,当查询确定要查询哪些行之后, 首先解压缩(因为存储的时候会压缩),然后直接取出相关的行(定位到数组的指定位置),并运用聚合算子计算指标
目录
- 数据集
- 数据结构
- segment
数据集
- Druid中的数据表(称为数据源)是一个时间序列事件数据的集合,并分割到一组segment中,而每一个segment通常是0.5-1千万行。
- 在形式上,我们定义一个segment为跨越一段时间的数据行的集合。Segment是Druid里面的基本存储单元,复制和分布都是在segment基础之上进行的
- Druid将数据集分成三种类型: Timestamp column, Dimension columns(过滤数据), Metric columns(聚合和计算)
- 事件的定义: 时间戳
- Dimensions: (things to filter on) 参与事件过滤
- Metrics: (things to aggregate over) 要聚合的字段
- 以下面的事件为例,Druid会将publisher,advertiser,gender,country当做维度列,click和price当做指标列,如下:
timestamp publisher advertiser gender country click price
2011-01-01T01:01:35Z bieberfever.com google.com Male USA 0 0.65
2011-01-01T01:03:63Z bieberfever.com google.com Male USA 0 0.62
2011-01-01T01:04:51Z bieberfever.com google.com Male USA 1 0.45
2011-01-01T01:00:00Z ultratrimfast.com google.com Female UK 0 0.87
2011-01-01T02:00:00Z ultratrimfast.com google.com Female UK 0 0.99
2011-01-01T02:00:00Z ultratrimfast.com google.com Female UK 1 1.53 - ruid读取数据的入口并不会直接存储原始数据, 而是使用Roll-up这种first-level聚合操作压缩原始数据,如下:
timestamp publisher advertiser gender country impressions clicks revenue
2011-01-01T01:00:00Z ultratrimfast.com google.com Male USA 1800 25 15.70
2011-01-01T01:00:00Z bieberfever.com google.com Male USA 2912 42 29.18
2011-01-01T02:00:00Z ultratrimfast.com google.com Male UK 1953 17 17.31
2011-01-01T02:00:00Z bieberfever.com google.com Male UK 3194 170 34.01 用SQL表示类似于对时间撮和所有维度列进行分组,并以原始的指标列做常用的聚合操作
GROUP BY timestamp, publisher, advertiser, gender, country
:: impressions = COUNT(1), clicks = SUM(click), revenue = SUM(price)为什么不存原始数据? 因为原始数据量可能非常大,对于广告的场景,一秒钟的点击数是以千万计数. 如果能够在读取数据的同时就进行一点聚合运算,就可以大大减少数据量的存储.这种方式的缺点是不能查询单条事件,也就是你无法查到每条事件具体的click和price值了.由于后面的查询都将以上面的查询为基础,所以Roll-up的结果一定要能满足查询的需求.通常count和sum就足够了,因此Rollup的粒度是你能查询的数据的最小时间单位. 假设每隔1秒Rollup一次,后面的查询你最小只能以一秒为单位,不能查询一毫秒的事件.默认的粒度单位是ms.
- Druid的分片是Segment文件. Druid首先总是以时间撮进行分片, 因为事件数据总是有时间撮. 假设以小时为粒度创建下面的两个Segment文件

- 在几乎所有的NoSQL中都有数据分片的概念,比如ES的分片,HBase的Region,都表示的是数据的存储介质.为什么要进行分片,因为数据大了,不能都存成一个大文件吧,所以要拆分成小文件以便于快速查询. 伴随拆分通常都有合并小文件
- 从Segment文件的名称可以看出它包含的数据一定是在文件名称对应的起始和结束时间间隔之内的
- Segment文件名称的格式:dataSource_interval_version_partitionNumber.最后一个分区号是当同一个时间撮下数据量超过阈值要分成多个分区了
- 分片和分区都表示将数据进行切分. 分片是将不同时间撮分布在不同的文件中, 而分区是相同时间撮放不下了,分成多个分区
- 巧合的是Kafka中也有Segment和Partition的概念.Kafka的Partition是topic物理上的分组,一个topic可以分为多个partition,它的partition物理上由多个segment组成.即Partition包含Segment,而Druid是Segment包含Partition.
数据结构
- 维度列因为要支持过滤和分组,每一个维度列的数据结构包含了三部分:
- 值到ID的Map映射
- 列的值列表, 存储的是上一步对应的ID
- 倒排索引
- 示例进行说明:

- 代表这一维度列的数据结构如下:
1: Dictionary that encodes column values
{
"Justin Bieber": 0,
"Ke$ha": 1
} 2: Column data
[0,
0,
1,
1] 3: Bitmaps - one for each unique value of the column
value="Justin Bieber": [1,1,0,0]
value="Ke$ha": [0,0,1,1] 注意: 在最坏情况下前面两种会随着数据量的大小而线性增长. 而BitMap的大小则等于数据量大小 * 列的个数.
结构说明
- 字典表的key都是唯一的, 所以Map的key是unique的column value, Map的value从0开始不断增加. 示例数据的page列只有两个不同的值. 所以为Bieber编号0, Ke$ha编号为1.
Key |Value
---------------|-----
Justin Bieber |0
Ke$ha |1
- 列的数据: 要保存的是每一行中这一列的值, 值是ID而不是原始的值. 因为有了上面的Map字典, 所以有下面的对应关系,这样列的值列表直接取最后一列: [0,0,1,1],
rowNum page ID
1 Justin Bieber 0
2 Justin Bieber 0
3 Ke$ha 1
4 Ke$ha 1
- BitMap的key是第一步Map的key(列的原始值). value数组的每个元素表示指定列的某一行是否包含/存在/等于当前key. 注意: BitMap保存的value数组只有两个值: 1和0, 1表示这一行包含或等于BitMap的key, 0表示不存在/不包含/不等于,如下:
第一行的page列值为Justin Bieber/列值为Justin Bieber的在第一行里
^
|
value="Justin Bieber": [1,1,0,0]
value="Ke$ha": [0,0,1,1]
^
|
第一行的page列值不是Ke$ha 这种存储方式, 如果unique重复的列很少,比如page列的每一个值都是不同的. BitMap就会是一个稀疏矩阵.
A: [1,0,0,0,0,0,0,0,0,0,0]
B: [0,1,0,0,0,0,0,0,0,0,0]
C: [0,0,1,0,0,0,0,0,0,0,0]
D: [0,0,0,1,0,0,0,0,0,0,0]
E: [0,0,0,0,1,0,0,0,0,0,0]unique的重复数量很少也叫做high cardinality,表示基数很高,不同列的数量很多,列值相同的记录数很少.
稀疏矩阵对于BitMap而言却是有优点的,因为越是稀疏,它可以被压缩的比例越大,最后存储的空间越少(相对原始数据).
- 上面只是针对page列的BitMap, 对于其他的维度列, 都有自己的BitMap! 即每一个维度列都有一个BitMap.
segment
- 数据进入到Druid首先会进行索引, 这给予了Druid一个机会可以进行分析数据, 添加索引结构, 压缩, 为查询优化调整存储结构
- 转换为列式结构
- 使用BitMap索引
- 使用不同的压缩算法
- 索引的结果是生成Segment文件,Segment中除了保存不同的维度和指标,还保存了这些列的索引信息
- Druid将索引数据保存到Segment文件中,Segment文件根据时间进行分片. 最基本的设置中, 每一个时间间隔都会创建一个Segment文件
- 这个时间间隔的长度配置在granularitySpec的segmentGranularity参数.为了Druid工作良好,通常Segment文件大小为300-700M
- 前面Roll-up时也有一个时间粒度:queryGranularity指的是在读取时就进行聚合.segmentGranularity则是用于分片进来之后的数据.
Durid(二): 数据集及存储的更多相关文章
- Spring Boot 揭秘与实战(二) 数据存储篇 - 声明式事务管理
文章目录 1. 声明式事务 2. Spring Boot默认集成事务 3. 实战演练4. 源代码 3.1. 实体对象 3.2. DAO 相关 3.3. Service 相关 3.4. 测试,测试 本文 ...
- Spring Boot 揭秘与实战(二) 数据存储篇 - ElasticSearch
文章目录 1. 版本须知 2. 环境依赖 3. 数据源 3.1. 方案一 使用 Spring Boot 默认配置 3.2. 方案二 手动创建 4. 业务操作5. 总结 4.1. 实体对象 4.2. D ...
- Spring Boot 揭秘与实战(二) 数据存储篇 - MongoDB
文章目录 1. 环境依赖 2. 数据源 2.1. 方案一 使用 Spring Boot 默认配置 2.2. 方案二 手动创建 3. 使用mongoTemplate操作4. 总结 3.1. 实体对象 3 ...
- Spring Boot 揭秘与实战(二) 数据存储篇 - Redis
文章目录 1. 环境依赖 2. 数据源 2.1. 方案一 使用 Spring Boot 默认配置 2.2. 方案二 手动创建 3. 使用 redisTemplate 操作4. 总结 3.1. 工具类 ...
- Spring Boot 揭秘与实战(二) 数据存储篇 - JPA整合
文章目录 1. 环境依赖 2. 数据源 3. 脚本初始化 4. JPA 整合方案一 通过继承 JpaRepository 接口 4.1. 实体对象 4.2. DAO相关 4.3. Service相关 ...
- Spring Boot 揭秘与实战(二) 数据存储篇 - MyBatis整合
文章目录 1. 环境依赖 2. 数据源3. 脚本初始化 2.1. 方案一 使用 Spring Boot 默认配置 2.2. 方案二 手动创建 4. MyBatis整合5. 总结 4.1. 方案一 通过 ...
- Spring Boot 揭秘与实战(二) 数据存储篇 - 数据访问与多数据源配置
文章目录 1. 环境依赖 2. 数据源 3. 单元测试 4. 源代码 在某些场景下,我们可能会在一个应用中需要依赖和访问多个数据源,例如针对于 MySQL 的分库场景.因此,我们需要配置多个数据源. ...
- Spring Boot 揭秘与实战(二) 数据存储篇 - MySQL
文章目录 1. 环境依赖 2. 数据源3. 脚本初始化 2.1. 方案一 使用 Spring Boot 默认配置 2.2. 方案二 手动创建 4. 使用JdbcTemplate操作5. 总结 4.1. ...
- 二叉树的二叉链表存储结构及C++实现
前言:存储二叉树的关键是如何表示结点之间的逻辑关系,也就是双亲和孩子之间的关系.在具体应用中,可能要求从任一结点能直接访问到它的孩子. 一.二叉链表 二叉树一般多采用二叉链表(binary linke ...
随机推荐
- angular 三目运算符 需要换色或style
解决方法:直接简单粗暴写两个.有点挫,但实现需求了. <font style="color:red" ng-show="boxlist.lineTitle.leng ...
- Head First 设计模式 --8 模板方法模式 别找我,我会找你
模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤.设计原则:1.封装变化2.多用组合少用集成3.针对接口变 ...
- 7 -- Spring的基本用法 -- 7...
7.7 创建Bean的3种方式 ① 调用构造器创建Bean. ② 调用静态工厂方法创建Bean. ③ 调用实例工厂方法创建Bean. 7.7.1 使用构造器创建Bean实例. 使用构造器来创建Bean ...
- 《高级Web应用程序设计》课程学习资料
任务1:什么是ASP.NET MVC 1.1 ASP.NET MVC简介 1.2 认识ASP.NET MVC项目结构 1.3 ASP.NET MVC生命周期 任务2:初识ASP.NET MVC项目开 ...
- <转>下一代Asp.net开发规范OWIN(1)—— OWIN产生的背景以及简单介绍
2014-09-04 07:22 by JustRun http://www.cnblogs.com/JustRun1983/p/3955238.html 随着VS2013的发布,微软在Asp.Net ...
- 《SICP》读后感:关于软件本质的一点思考
摘要:软件本身不是目的,人类的需求才是目的,而软件只是达到目的的手段. 软件的本质在于控制复杂性,这个复杂性并非来自于计算机,也并非来自于现实世界,而是来自于人类的思维和知识体系. 软件被使用的广泛性 ...
- 【lattice软核】MICO8流程
The LatticeMico System software is composed of three bundled applications: Mico System Builder (MS ...
- bzoj 2768: [JLOI2010]冠军调查
#include<cstdio> #include<iostream> #define M 100000 #include<cstring> using names ...
- 启动本地Oracle
net start OracleOraDb10g_home1TNSListenernet start OracleServiceORCL第一个是监听服务第二个是数据库服务
- jQuery.ajax() 函数详解
jQuery.ajax()函数用于通过后台HTTP请求加载远程数据. jQuery.ajax()函数是jQuery封装的AJAX技术实现,通过该函数,我们无需刷新当前页面即可获取远程服务器上的数据. ...