Kylin官网:
https://kylin.apache.org/
http://kylin.apache.org/cn/
一、概述
- Apache Kylin是一个开源的、分布式的分析性数据库。Kylin是为了解决MapReduce编码复杂且效率低的问题。
- Kylin的核心思想是利用空间换取时间,采用预处理方法,列出用户所有可能的查询SQL,提前计算各个维度的聚合结果并持久化保存到HBase,并且Kylin支持复杂的join操作。
- Kylin支持亚秒级的查询,并且支持高并发。
二、Kylin的特点
- SQL接口
Kylin主要的对外接口就是以SQL的形式提供的。SQL简单易用的特性极大地降低了Kylin的学习成本,不论是数据分析师还是Web开发程序员都能从中收益。
- 支持海量数据集
不论是Hive、SparkSQL,还是Impala,它们的查询时间都随着数据量的增长而线性增长。而Apache Kylin使用预计算技术打破了这一点。Kylin在数据集规模上的局限性主要取决于维度的个数和基数,而不是数据集的大小,所以Kylin能更好地支持海量数据集的查询。
- 亚秒级响应
受益于预计算技术,Kylin的查询速度非常快,因为复杂的连接、聚合等操作都在Cube的构建过程中已经完成了。
- 水平扩展
Apache Kylin同样可以使用集群部署方式进行水平扩展。但部署多个节点只能提高Kylin处理查询的能力,而不能提升它的预计算能力。
- 可视化集成
Kylin提供与BI工具的整合能力,如Tableau,PowerBI/Excel,MSTR,QlikSense,Hue和SuperSet。
- 构建多维立方体(Cube)
用户能够在Kylin里为百亿以上数据集定义数据模型并构建立方体。
3、应用场景
- 如果用户有一个巨大的表 (如:超过 1 亿行) 与维表进行 JOIN,而且查询需要在仪表盘、交互式报告、BI (商务智能) 中完成,用户并发数量为几十个或者几百个,那么 Kylin 是最好的选择。
- Kylin 其他的典型应用场景如下:
- 用户数据存在于Hadoop HDFS中,利用Hive将HDFS文件数据以关系数据方式存取,数据量巨大,在500G以上
- 每天有数G甚至数十G的数据增量导入
- 有30个以内较为固定的分析维度(价格,地区,销售量…)
- 备注: Cube的最大物理维度是63,官方不推荐使用大于30个维度的Cube,会引起维度灾难,因为Cube的Cubeid数量 = 2 ^ n (n为维度数量),会发生指数爆炸,在后边讲到会有所体会。
四、区分Druid和Kylin
- Druid:实时的OLAP数据库,一般是大量实时数据流–> Kafka–>Druid(里面有DataServer数据存储)
- Kylin:亚秒级的OLAP数据仓库,建立在HDFS之上,一般HDFS–>Hive–>Kylin(数据存在HDFS,还有一部分在HBase)
- 注意:Druid和Kylin都支持简单查询,分组,聚合,排序等操作, 但是Druid不支持多表join! 而Kylin支持多表join
五、Kylin原理
架构图
名词介绍
Apache Kylin的工作原理本质上是MOLAP(多维度立方体分析)。 本质上就是根据维度和度量来构建Cube
- 维度:就是观察数据的角度。例如,我们可以从时间维度观察某个商品的销售数据,也可以从地区维度观察某个维度的商品销售数据。
- 度量:被聚合的统计值,也称作指标,是聚合运算的结果
- 字段:一个数据表或者数据模型上的字段要么是维度,要么是度量(指标)。
- Cubeid :维度之间的不同排列组合结果称之为Cubeid。一个Cube共有 2 ^ n(n表示维度个数)个Cubeid
- Cube:所有维度组合的Cubeid作为一个整体,被称作Cube(立方体)。一个Cube就是许多按照维度聚合的物化视图的集合。
- Segment:
- 1.表示一段时间内的源数据的预计算结果。
- 2.每个Segment用起始时间和结束时间来标志。
- 3.一个Segment的起始时间等于它之前的那个Segment的结束时间。同理,它的结束时间等于它后面那个Segment的起始时间。Segment [ 起始时间,结束时间 ),前闭后开。
- 4.同一个Cube下不同的Segment除了背后的源数据日期不同之外,其他如结构定义、构建过程、优化方法、存储方式等都完全相同。
- 5.其实就是指定时间范围内的Cube,可以理解为是某个Cube的分区。
图解
上图表示有三个维度的Cube :手机品牌维度、时间维度、地区维度。
转换成表方便观看
手机品牌(维度) | 地区(维度) | 时间(维度) | 销售额(度量) |
苹果 | 中国 | 2010 | 0.6M |
苹果 | 美国 | 2010 | 0.6M |
苹果 | 日本 | 2010 | 0.6M |
华为 | 中国 | 2010 | 0.6M |
华为 | 美国 | 2010 | 0.9M |
华为 | 日本 | 2010 | 0.7M |
小米 | 中国 | 2010 | 0.9M |
小米 | 美国 | 2010 | 0.9M |
小米 | 日本 | 2010 | 0.9M |
苹果 | 中国 | 2011 | 0.6M |
苹果 | 美国 | 2011 | 0.6M |
苹果 | 日本 | 2011 | 0.6M |
华为 | 中国 | 2011 | 0.6M |
华为 | 美国 | 2011 | 0.9M |
华为 | 日本 | 2011 | 0.7M |
小米 | 中国 | 2011 | 0.9M |
小米 | 美国 | 2011 | 0.9M |
小米 | 日本 | 2011 | 0.9M |
苹果 | 中国 | 2012 | 0.6M |
苹果 | 美国 | 2012 | 0.6M |
苹果 | 日本 | 2012 | 0.6M |
华为 | 中国 | 2012 | 0.6M |
华为 | 美国 | 2012 | 0.9M |
华为 | 日本 | 2012 | 0.7M |
小米 | 中国 | 2012 | 0.9M |
小米 | 美国 | 2012 | 0.9M |
小米 | 日本 | 2012 | 0.9M |
一共有三个维度,分别为:手机品牌维度、时间维度、地区维度。则共有2 ^ 3 = 8个Cubeid :
- 无维度 ,只有度量 ------Cubeid
- 手机品牌维度 的销售额(度量) ------Cubeid
- 地区维度 的销售额(度量) ------Cubeid
- 时间维度 的销售额(度量) ------Cubeid
- 手机品牌维度、地区维度 的销售额(度量) ------Cubeid
- 手机品牌维度、时间维度 的销售额(度量) ------Cubeid
- 地区维度、时间维度 的销售额(度量) ------Cubeid
- 手机品牌维度、地区维度、时间维度 的销售额(度量) ------Cubeid
上面所有的Cubeid组成一个Cube
我们将上表通过时间划分为两个Segment
- 第一个Segment的起止时间为[ 2010,2012 )
手机品牌(维度) | 地区(维度) | 时间(维度) | 销售额(度量) |
苹果 | 中国 | 2010 | 0.6M |
苹果 | 美国 | 2010 | 0.6M |
苹果 | 日本 | 2010 | 0.6M |
华为 | 中国 | 2010 | 0.6M |
华为 | 美国 | 2010 | 0.9M |
华为 | 日本 | 2010 | 0.7M |
小米 | 中国 | 2010 | 0.9M |
小米 | 美国 | 2010 | 0.9M |
小米 | 日本 | 2010 | 0.9M |
苹果 | 中国 | 2011 | 0.6M |
苹果 | 美国 | 2011 | 0.6M |
苹果 | 日本 | 2011 | 0.6M |
华为 | 中国 | 2011 | 0.6M |
华为 | 美国 | 2011 | 0.9M |
华为 | 日本 | 2011 | 0.7M |
小米 | 中国 | 2011 | 0.9M |
小米 | 美国 | 2011 | 0.9M |
小米 | 日本 | 2011 | 0.9M |
- 第二个Segment的起止时间为[ 2012,2013 )
手机品牌(维度) | 地区(维度) | 时间(维度) | 销售额(度量) |
苹果 | 中国 | 2012 | 0.6M |
苹果 | 美国 | 2012 | 0.6M |
苹果 | 日本 | 2012 | 0.6M |
华为 | 中国 | 2012 | 0.6M |
华为 | 美国 | 2012 | 0.9M |
华为 | 日本 | 2012 | 0.7M |
小米 | 中国 | 2012 | 0.9M |
小米 | 美国 | 2012 | 0.9M |
小米 | 日本 | 2012 | 0.9M |
工作原理
- Apache Kylin的公园里是对数据模型做Cube预计算,并且利用计算结果快速查询。具体工作过程如下。
- 指定数据模型,定义维度和度量。
- 预计算Cube,计算所有Cuboid并保存为物化视图(存储到hbase中)。
- 执行查询时,读取Cuboid,运算,产生查询结果。
为什么Kylin针对海量数据的OLAP能够做的亚秒级分析:
- Kylin在构建Cube的时候已经做了预计算。
- Kylin的查询过程不会扫描原始记录,而是通过预计算结果(在HBase中)预先完成表的关联、聚合等复杂运算。
- 利用预计算的结果来执行查询,相比非预计算的查询技术,其速度一般要快一到两个数量级,在超大的数据集上优势更明显。
- 数据集达到千亿乃至万亿级别时,Kylin的速度可以超越其他非预计算技术1000倍以上。
六、Cube的全量构建与增量构建
Cube支持全量构建、增量构建,全量查询、增量查询。
两者构建时的区别
- 全量构建
- Cube中只存在唯一的一个Segment
- 该Segment没有分割时间的概念,也就没有起始时间和结束时间
- 对于全量构建来说,每当需要更新Cube数据的时候,它不会区分历史数据和新加入的数据,也就是说,在构建的时候会导入并处理所有的原始数据。
- 增量构建
- 只会导入新Segment指定的时间区间内的原始数据,并只对这部分原始数据进行预计算。
- 对于之前的历史数据就不再进行预计算了,因为之前已经计算过了
两者查询时的区别
- 全量构建时查询Cube
- 查询引擎只需向存储引擎访问单个Segment所对应的数据,无需进行Segment之间的结果聚合
- 为了加强性能,单个Segment的数据也有可能被HBase分到的多个HRegion上,查询引擎可能仍然需要对单个Segment不同HRegion的数据做进一步的聚合
增量构建时查询Cube
- 由于不同时间的数据分布在不同的Segment之中,查询引擎需要向存储引擎请求读取各个Segment的数据
- 增量构建的Cube上的查询会比全量构建的做更多的运行时聚合,通常来说增量构建的Cube上的查询会比全量构建的Cube上的查询要慢一些
总结:
- 对于小数据量的Cube,或者经常需要全表更新的Cube,使用全量构建需要更少的运维精力,以少量的重复计算降低生产环境中的维护复杂度。
- 对于大数据量的Cube,例如,对于一个包含两年历史数据的Cube,如果需要每天更新,那么每天为了新数据而去重复计算过去两年的数据就会变得非常浪费,在这种情况下需要考虑使用增量构建
全量构建 | 增量构建 |
每次更新时都需要更新整个数据集 | 每次只对需要更新的时间范围进行更新,因此离线计算量相对较小 |
查询时不需要合并不同Segment的结果 | 查询时需要合并不同Segment的结果,因此查询性能会受影响 |
不需要后续的Segment合并 | 累计一定量的Segment之后,需要进行合并 |
适合小数据量或全表更新的Cube | 适合大数据量的Cube |
七、Kylin的优化
- 优化判断:
- 检查Cubeid数量:Apache Kylin提供了一个简单的工具,检查Cube中哪些 Cubeid最终被预计算了,称这些Cubeid为被物化的Cuboid,该工具还能给出每个Cuboid所占空间的估计值。Cubeid。由于该工具需要在对数据进行一定阶段的处理之后才能估算Cuboid的大小,因此一般来说只能在Cube构建完毕之后再使用该工具。
- Web GUI会提示Cube的源数据大小,以及当前Cube的大小除以源数据大小的比例,称为膨胀率。一般来说,Cube的膨胀率应该在==0%~1000%==之间,如果一个Cube的膨胀率超过1000%,那么应当开始挖掘其中的原因。通常,膨胀率高有以下几个方面的原因:
- Cube中的维度数量较多,且没有进行很好的Cuboid剪枝优化,导致Cuboid数量极多。
- Cube中存在较高基数的维度,导致包含这类维度的每一个Cuboid占用的空间都很大,这些Cuboid累积造成整体Cube体积变大
- 存在比较占用空间的度量,例如Count Distinct,因此需要在Cuboid的每一行中都为其保存一个较大度量数据,最坏的情况将会导致Cuboid中每一行都有数十KB,从而造成整个Cube的体积变大。
1.选择增量管理(上面介绍过)
2.Kylin碎片管理
- 增量构建的问题
日积月累,增量构建的Cube中的Segment越来越多,该Cube的查询性能也会越来越慢,因为需要在单点的查询引擎中完成越来越多的运行时聚合。为了保持查询性能:
- 需要定期地将某些Segment合并在一起
- 或者让Cube根据Segment保留策略自动地淘汰那些不会再被查询到的陈旧Segment
- 管理Cube碎片
上述案例,每天都会生成一个Segment,对应就是HBase中的一张表。增量构建的Cube每天都可能会有新的增量。这样的Cube中最终可能包含上百个Segment,这将会导致Kylin性能受到严重的影响。
- 从执行引擎的角度来说,运行时的查询引擎需要聚合多个Segment的结果才能返回正确的查询结果
- 从存储引擎的角度来说,大量的Segment会带来大量的文件,给存储空间的多个模块带来巨大的压力,例如Zookeeper、HDFS Namenode等
因此,有必要采取措施控制Cube中Segment的数量。
- 四种方式具体操作实现看官网
- 手动合并Segment
- 手动删除Segment
- 自动合并Segment
- 自动删除Segment
3.Cubeid剪枝优化
为什么要进行Cuboid剪枝优化
将以减少Cuboid数量为目的的Cuboid优化统称为Cuboid剪枝。在没有采取任何优化措施的情况下,Kylin会对每一种维度的组合进行预计算,每种维度的组合的预计算结果被称为Cuboid。
- 如果有4个维度,可能最终会有2^4 =16个Cuboid需要进行预计算和存储。但在实际开发中,用户的维度数量一般远远大于4个。
- 如果有10个维度,那么没有经过任何优化的Cube就会存在2^10 =1024个Cuboid
- 如果有20个维度,那么Cube中总共会存在2^20 =104 8576个Cuboid
这样的Cuboid的数量就足以让人想象到这样的Cube对构建引擎、存储引擎压力非常巨大。
因此,在构建维度数量较多的Cube时,尤其要注意Cube的剪枝优化。
Cube的剪枝优化是一种试图减少额外空间占用的方法,这种方法的前提是不会明显影响查询时间。在做剪枝优化的时候,
- 需要选择跳过那些“多余”的Cuboid
- 有的Cuboid因为查询样式的原因永远不会被查询到,因此显得多余
- 有的Cuboid的能力和其他Cuboid接近,因此显得多余
- 方法一:指定衍生维度Drived
将部分维度指定为衍生维度,Kylin在预聚合时会排除这些维度而是使用维度表的主键来代替它们创建Cuboid。后续查询的时候,再基于主键的聚合结果,再进行一次查询/聚合。
优化效果:维度表的N个维度组合成的cuboid个数会从2的N次方降为2。
不适用的场景:如果从维度表主键到某个维度表维度所需要的聚合工作量非常大,此时作为一个普通的维度聚合更合适,否则会影响Kylin的查询性能
- 方法二:指定聚合组
- 聚合组介绍
- 聚合组(Aggregation Group)是一种更强大的剪枝工具
- 聚合组假设一个Cube的所有维度均可以根据业务需求划分成若干组
- 同一个组内的维度更可能同时被同一个查询用到,每个分组的维度集合均是Cube所有维度的一个子集
- 不同的分组各自拥有一套维度集合,它们可能与其他分组有相同的维度,也可能没有相同的维度
- 每个分组各自独立地根据自身的规则贡献出一批需要被物化的Cuboid,所有分组贡献的Cuboid的并集就成为了当前Cube中所有需要物化的Cuboid的集合
- 不同的分组有可能会贡献出相同的Cuboid,构建引擎会察觉到这点,并且保证每一个Cuboid无论在多少个分组中出现,它都只会被物化一次
对于每个分组内部的维度,用户可以使用如下三种可选的方式定义它们之间的关系,具体如下:
强制维度(Mandatory)
(1) 如果一个维度被定义为强制维度,那么这个分组产生的所有Cuboid中每一个Cuboid都会包含该维度。所有cuboid必须包含的维度,不会计算不包含强制维度的cuboid
(2) 每个分组中都可以有0个、1个或多个强制维度
(3) 如果根据这个分组的业务逻辑,则相关的查询一定会在过滤条件或分组条件中,因此可以在该分组中把该维度设置为强制维度
(4) 适用场景
可以将确定在查询时一定会使用的维度设为强制维度。例如,时间维度。
(5) 优化效果
将一个维度设为强制维度,则cuboid个数直接减半
层级维度(Hierarchy)
(1) 每个层级包含两个或更多个维度
(2) 假设一个层级中包含D1,D2…Dn这n个维度,那么在该分组产生的任何Cuboid中,这n个维度只会以(),(D1),(D1,D2)…(D1,D2…Dn)这n+1种形式中的一种出现
(3) 每个分组中可以有0个、1个或多个层级,不同的层级之间不应当有共享的维度
(4) 如果根据这个分组的业务逻辑,则多个维度直接存在层级关系,因此可以在该分组中把这些维度设置为层级维度
(5) 使用场景
年,月,日;国家,省份,城市这类具有层次关系的维度
(6) 优化效果
将N个维度设置为层次维度,则这N个维度组合成的cuboid个数会从2的N次方减少到N+1
联合维度(Joint)
(1) 每个联合中包含两个或更多个维度,如果某些列形成一个联合,那么在该分组产生的任何Cuboid中,这些联合维度要么一起出现,要么都不出现
(2) 每个分组中可以有0个或多个联合,但是不同的联合之间不应当有共享的维度(否则它们可以合并成一个联合)。如果根据这个分组的业务逻辑,多个维度在查询中总是同时出现,则可以在该分组中把这些维度设置为联合维度
(3) 适用场景
可以将确定在查询时一定会同时使用的几个维度设为一个联合维度
(4) 优化效果
将N个维度设置为联合维度,则这N个维度组合成的cuboid个数会从2的N次方减少到1