Apache Kylin : Analytical Data Warehouse for Big Data
Welcome to Kylin Wiki.
背景
为什么需要全局字典
在OLAP数据分析领域,去重计数(Count distinct)是非常常见的需求,而根据去重结果的要求又分为近似去重和精确去重。
在大规模数据集下,要想做到精确去重还要保证查询快速响应还是很有挑战性的。我们知道精确去重经常用到的处理方式就是位图法(Bit map)。对于整型数据,我们可以将统计信息保存在Bit map中,但是实际处理的数据中除了整型还会有String等其他类型,如果想做到精确去重首先就需要构建一个字典来为这些数据进行统一映射,然后再通过位图法进行统计。
我们都知道Kylin通过预计算技术来加速大数据分析。在增量构建Cube的时候,为了避免不同的segment单独构建字典导致最终去重结果出错,一个Cube中所有的Segment会使用同一个字典,也就是全局字典(Global Dictionary)。
全局字典的演变
Kylin从1.5.3版本就开始支持全局字典功能,但是这时的构建方式也具有明显的缺陷:
- 全局字典在Job Server上进行单点构建,随着数据增多构建时长变得不可控
- 随着数据积累,全局字典的构建对Kylin 构建节点的内存的需求会不断增多
- 受限于整型最大数量的限制
其实在Kylin3.1中已经加入了基于Hive的分布式全局字典构建,它已经解决了以上问题,详情可以查看Kylin 分布式全局字典 但是Kylin4为了适应全新的构建查询引擎,基于spark实现了另外一种分布式构建全局字典的方式,今天我们就来详细描述一下Kylin 4.0的全局字典是如何实现的。
基于Spark的全局字典
Kylin 4.0构建全局字典的方式基于Spark进行分布式的编码处理,减小了单机节点的压力,构建字典数量能够突破整型最大数量的限制。
设计与原理
全局字典结构设计
- 每一次构建任务会生成一份新的全局字典
- 每次新的构建任务的字典按版本号进行保存, 旧的全局字典会被逐渐删除
- 一份字典包括一份meta数据文件和多个字典文件, 每个字典文件称之为桶(Bucket)
- 每个桶分为两个映射( Map<Object, Long>), 两者合并为完整的映射关系
构建步骤
- 通过 Spark 创建平表并获取需精确去重列的 distinct 值
- 将数据分配到多个桶(Bucket)中,对每一个桶内的数据进行编码
- 为当前构建任务分配版本号
- 保存字典文件和 metadata数据(桶数量和桶的 offset 值)
桶
Kylin引入了桶这一概念,可以理解为在处理数据的时候,将数据分到若干个桶(即多个分区)中进行并行处理。 第一次构建字典的时候会对每个桶内的值从1开始编码,在所有桶的编码完成之后再根据每个桶的offset值进行整体字典值的分配。在代码中两次编码是通过两个HashMap进行存储的,其中一个存储桶内相对的字典值,另一个存储所有桶之间绝对的字典值。
版本控制
因为构建任务可能会并发进行,所以全局字典构建加入了版本控制来。版本号是基于时间戳控制的,新的构建任务会使用最新的时间戳,并且会基于之前版本的字典数据继续编码。 字典在HDFS上按版本存储如下图所示。
初次构建
- 计算桶的大小
取需要构建字典的数量处理单个桶阈值和桶数量默认值的最大值。 - 创建桶并分配数据进行编码
- 生成meta文件记录桶的offsets
以下是相关配置项及其默认值。
kylin.dictionary.globalV2-min-hash-partitions=10 kylin.dictionary.globalV2-threshold-bucket-size=500000
非初次构建
- 根据字典数量确定桶是否需要扩容
- 已编码的字典值对扩容后的桶进行重新分配
- 读取之前最新版本的字典数据,并分配到各个桶中
- 将新的值分配到桶中
- 前一次构建的字典值不会改变