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 ...
随机推荐
- node.js基础 1之基本概念常识
node.js 好牛逼的样子哦 很火,很腻害~~~~ 有关node.js的版本常识: 一般用最新的稳定版本,非稳定版本用于测试,其中包括api的不稳定等. 起一个web服务器: ndoejs可以自定义 ...
- Find a point on a 'line' between two Vector3
Find a point on a 'line' between two Vector3http://forum.unity3d.com/threads/find-a-point-on-a-line- ...
- SPSS数据分析—非参数检验
统计学的假设检验可以分为参数检验和非参数检验,参数检验都是根据一些假设条件推算而来,当这些假设条件无法满足的时候,参数检验的效能会大打折扣,甚至出现错误的结果,而非参数检验通常是没有假设条件的,因此应 ...
- oracle中如何创建dblink
当用户要跨本地数据库,访问另外一个数据库表中的数据时,本地数据库中必须创建了远程数据库的dblink,通过dblink本地数据库可以像访问本地数据库一样访问远程数据库表中的数据.下面讲介绍如何在本地数 ...
- Js 冒泡事件阻止
Js 冒泡事件阻止 1. 事件目标 现在,事件处理程序中的变量event保存着事件对象.而event.target属性保存着发生事件的目标元素.这个属性是DOM API中规定的,但是没有被所有浏览 ...
- 读《程序员的SQL金典》[2]--函数
一.数学函数 1.RAND SELECT RAND () ---0.302870228294199 取0-1之间的随机小数. 2.小数取整 CEILINT(data)舍掉小数部分并向上取整. FLOO ...
- HighCharts绘制图表
<div id="TradeMoney"></div> <script> $(function () { initData(); }); fun ...
- 求助,JS 方法调用的问题,写了返回值,确调用时返回Undefined(新人第一次写)
文本框txtUserName 的Onbulr 绑定方法 txtUserNameOnblur 这可以正常执行 而下面的btnRegister 的click 事件则无法正常调用 txtUserNa ...
- 拓扑编号 vijos1790
题意就是拓扑排序,要求1的序号尽可能小,然后2的序号尽可能小,3,4... 一开始很容易想到直接贪心,每次选一个入度为0的点,如果有多个,就选编号最小的那个,但是很容易找到反例. 看了下题解,应该是反 ...
- 实现手机扫描二维码页面登录,类似web微信-第三篇,手机客户端
转自:http://www.cnblogs.com/fengyun99/p/3541254.html 上一篇,介绍了二维码生成的机制,紧接着,我们就要开发手机客户端来识别这个二维码. 二维码,实际上是 ...