基础知识储备-大数据-Apache Kylin

Apache Kylin是一种针对Hadoop的大数据OLAP引擎,通过预计算技术提高交互式查询速度。它采用多维立方体设计,通过预计算Cuboid优化查询性能。文章介绍了Kylin的诞生背景、工作原理、技术架构以及如何处理维度基数、cube构建、增量构建、自动合并等关键特性。此外,还讨论了Cuboid剪枝优化、空间与时间的平衡策略,以及衍生维度和聚合组的使用,以降低存储开销并提升查询效率。
摘要由CSDN通过智能技术生成

Apache Kylin介绍

       Apache Kylin是一种分布式分析引擎, 提供Hadoop之上的标准SQL查询接口及多维分析(OLAP)功能。是Hadoop大数据平台上的一个开源OLAP引擎。它采用 多维立方体预计算技术,可以将大数据的SQL查询速度提升到亚秒级别。
     联机实时分析(OnlineAnalytical Processing, OLAP )技术是快速响应多维分析(Multidimensionalanalysis, MDA)的一种解决方案。

诞生背景:

2013年年初,eBay内部使用的传统数据仓库及商业智能平台应用碰 到了瓶颈,即传统的架构只支持垂直扩展,通过在一台机器上增加CPU和内存等资源来提升数据处理能力,相对于数据指数级的增长,单机扩展 很快就达到了极限。另一方面,Hadoop大数据平台虽然能存储和批量处 理大规模数据,但与BI平台的连接技术依然不成熟,无法提供高效的交 互式查询

解决方式:

—“预计算”。应尽量多
地预先计算聚合结果,在查询时刻应尽量使用预算的结果得出查询结
果,从而避免直接扫描可能无限增长的原始记录。
举例来说,使用如下的SQL来查询10月1日那天销量最高的商品:
用传统的方法时需要扫描所有的记录,再找到10月1日的销售记录,
然后按商品聚合销售额,最后排序返回。假如10月1日有1亿条交易,那么 查询必须读取并累计至少1亿条记录,且这个查询速度会随将来销量的增 加而逐步下降。如果日交易量提高一倍到2亿,那么查询执行的时间可能
也会增加一倍。
而使用预计算的方法则会事先按维度[sell_date,item]计算 sum(sell_amount)并存储下来,在查询时找到10月1日的销售商品就可以 直接排序返回了。读取的记录数最大不会超过维度[sell_date,item]的组 合数。显然这个数字将远远小于实际的销售记录,比如10月1日的1亿条 交易包含了100万条商品,那么预计算后就只有100万条记录了,是原来
的百分之一。并且这些记录已经是按商品聚合的结果,因此又省去了运
行时的聚合运算。从未来的发展来看,查询速度只会随日期和商品数目
的增长而变化,与销售记录的总数不再有直接联系。假如日交易量提高 一倍到2亿,但只要商品的总数不变,那么预计算的结果记录总数就不会
变,查询的速度也不会变。

工作原理:

维度就是观察数据的角度。度量就是被聚合的统计值,也是聚合运算的结果.

给定一个数据模型,我们可以对其上的所有维度进行组合。对于N个 维度来说,组合的所有可能性共有2N 种。对于每一种维度的组合,将度 量做聚合运算,然后将运算的结果保存为一个物化视图,称为Cuboid。所 有维度组合的Cuboid作为一个整体,被称为Cube。所以简单来说,一个 Cube就是许多按维度聚合的物化视图的集合

下面来列举一个具体的例子。假定有一个电商的销售数据集,其中 维度包括时间(Time)、商品(Item)、地点(Location)和供应商(Supplier), 度量为销售额(GMV)。那么所有维度的组合就有24 =16种(如图1-3所 示),比如一维度(1D)的组合有[Time]、[Item]、[Location]、[Supplier]4种; 二维度(2D)的组合有[Time,Item]、[Time,Location]、[Time、Supplier]、 [Item,Location]、[Item,Supplier]、[Location,Supplier]6种;三维度(3D)的 组合也有4种;最后零维度(0D)和四维度(4D)的组合各有1种,总共就有 16种组合。

计算Cuboid,即按维度来聚合销售额。

具体工作过程:

1)指定数据模型,定义维度和度量。
2)预计算Cube,计算所有Cuboid并保存为物化视图。
3)执行查询时,读取Cuboid,运算,产生查询结果。

由于Kylin的查询过程不会扫描原始记录,而是通过预计算预先完成
表的关联、聚合等复杂运算,并利用预计算的结果来执行查询,因此相比
非预计算的查询技术,其速度一般要快一到两个数量级,并且这点在超 大的数据集上优势更明显。当数据集达到千亿乃至万亿级别时,Kylin的 速度甚至可以超越其他非预计算技术1000倍以上。

Apache Kylin的技术架构

数据源在左 侧,目前主要是Hadoop Hive,保存着待分析的用户数据。根据元数据的 定义,下方构建引擎从数据源抽取数据,并构建Cube。数据以关系表的形 式输入,且必须符合星形模型(Star Schema),MapReduce是当前主要的构建技术。构建后的Cube保存在右侧的存储引 擎中,一般选用HBase作为存储。

完成了离线构建之后,用户可以从上方查询系统发送SQL进行查询 分析。Kylin提供了各种Rest API、JDBC/ODBC接口。无论从哪个接口进 入,SQL最终都会来到Rest服务层,再转交给查询引擎进行处理。这里需 要注意的是,SQL语句是基于数据源的关系模型书写的,而不是Cube。 Kylin在设计时刻意对查询用户屏蔽了Cube的概念,分析师只需要理解简 单的关系模型就可以使用Kylin,没有额外的学习门槛,传统的SQL应用 也很容易迁移。查询引擎解析SQL,生成基于关系表的逻辑执行计划,然 后将其转译为基于Cube的物理执行计划,最后查询预计算生成的Cube并
产生结果。整个过程不会访问原始数据源。

 

维度的基数

维度的基数(Cardinality)指的是该维度在数据集中出现的不同值的 个数;例如“国家”是一个维度,如果有200个不同的值,那么此维度的基数 就是200。通常一个维度的基数会从几十到几万个不等,个别维度如“用 户ID”的基数会超过百万甚至千万。基数超过一百万的维度通常被称为超 高基数维度(Ultra High Cardinality,UHC),需要引起设计者的注意。
Cube中所有维度的基数都可以体现出Cube的复杂度,如果一个Cube 中有好几个超高基数维度,那么这个Cube膨胀的概率就会很高。在创建 Cube前需要对所有维度的基数做一个了解,这样就可以帮助设计合理的 Cube。计算基数有多种途径,最简单的方法就是让Hive执行一个count distinct的SQL查询;Kylin也提供了计算基数的方法,在2.3.1节中会进行介
绍。


cube构建

Kylin默认会把所有维度都放在同一个聚合组中;如果维度数较多(例 如>10),那么建议用户根据查询的习惯和模式,单击“New Aggregation Group+”,将维度分为多个聚合组。通过使用多个聚合组,可以大大降低 Cube中的Cuboid数量。下面来举例说明,如果一个Cube有(M+N)个维度, 那么默认它会有2m+n 个Cuboid;如果把这些维度分为两个不相交的聚合 组,那么Cuboid的数量将被减少为2m +2n 。
在单个聚合组中,可以对维度设置高级属性,如Mandatory、 Hierarchy、Joint等。这几种属性都是为优化Cube的计算而设计的,了解这 些属性的含义对日后更好地使用Cube至关重要。
Mandatory维度指的是那些总是会出现在Where条件或Group By语句 里的维度;通过将某个维度指定为Mandatory,Kylin就可以不用预计算那 些不包含此维度的Cuboid,从而减少计算量。
Hierarchy是一组有层级关系的维度,例如“国家”“省”“市”,这里 的“国家”是高级别的维度,“省”“市”依次是低级别的维度。用户会按高级
别维度进行查询,也会按低级别维度进行查询,但在查询低级别维度时,
往往都会带上高级别维度的条件,而不会孤立地审视低级别维度的数 据。例如,用户会单击“国家”作为维度来查询汇总数据,也可能单击“国 家”+“省”,或者“国家”+“省”+“市”来查询,但是不会跨越国家直接 Group By“省”或“市”。通过指定Hierarchy,Kylin可以省略不满足此模式的 Cuboid。

Joint是将多个维度组合成一个维度,其通常适用于如下两种情形。
·总是会在一起查询的维度。
·基数很低的维度。

各维度在Rowkeys中的顺序,对于查询的性能会产生较明显的影响。
在这里用户可以根据查询的模式和习惯,通过拖曳的方式调整各个维度 在Rowkeys上的顺序(如图2-17所示)。通常的原则是,将过滤频率较高的
列放置在过滤频率较低的列之前,将基数高的列放置在基数低的列之 前。这样做的好处是,充分利用过滤条件来缩小在HBase中扫描的范围,
从而提高查询的效率。

Cube的构建包含如下步骤,由任务引擎来调度执行。
1)创建临时的Hive平表(从Hive读取数据)。
2)计算各维度的不同值,并收集各Cuboid的统计数据。
3)创建并保存字典。
4)保存Cuboid统计信息。
5)创建HTable。
6)计算Cube(一轮或若干轮MapReduce)。
7)将Cube的计算结果转成HFile。
8)加载HFile到HBase。
9)更新Cube元数据。
10)垃圾回收。

 

增量构建

增量构建的Cube和全量构建的Cube在查询时也有不同。对于
增量构建的Cube,由于不同时间的数据分布在不同的Segment之中,因此 为了获得完整的数据,查询引擎需要向存储引擎请求读取各个Segment的
数据。当然,查询引擎会根据查询中的条件自动跳过不感兴趣的 Segment。对于全量构建的Cube,查询引擎只需要向存储引擎访问单个 Segment所对应的数据,从存储层返回的数据无需进行Segment之间的聚 合,但是这也并非意味着查询全量构建的Cube不需要查询引擎做任何额 外的聚合,为了加强性能,单个Segment的数据也有可能被分片存储到引 擎的多个分区上(参考第6章),从而导致查询引擎可能仍然需要对单个 Segment不同分区的数据做进一步的聚合。当然,整体来说,增量构建的 Cube上的查询会比全量构建的做更多的运行时聚合,而这些运行时聚合 都发生在单点的查询引擎之上,因此通常来说增量构建的Cube上的查询 会比全量构建的Cube上的查询要慢一些。

对于小数据量的Cube,或者经常需 要全表更新的Cube,使用全量构建需要更少的运维精力,以少量的重复
计算降低生产环境中的维护复杂度。而对于大数据量的Cube,例如,对于 一个包含两年历史数据的Cube,如果需要每天更新,那么每天为了新数
据而去重复计算过去两年的数据就会变得非常浪费,在这种情况下需要
考虑使用增量构建。

自动合并

“Auto Merge Thresholds”允许用户设置几个层级的时间阈值,层级越 靠后,时间阈值就越大。举例来说,用户可以为一个Cube指定(7天、28天) 这样的层级。每当Cube中有新的Segment状态变为READY的时候,就会触
发一次系统试图自动合并的尝试。系统首先会尝试最大一级的时间阈 值,结合上面的(7天、28天)层级的例子,首先查看是否能将连续的若干 个Segment合并成为一个超过28天的大Segment,在挑选连续Segment的过 程中,如果遇到已经有个别Segment的时间长度本身已经超过了28天,那 么系统会跳过该Segment,从它之后的所有Segment中挑选连续的累积超 过28天的Segment。如果满足条件的连续Segment还不能够累积超过28天,
那么系统会使用下一个层级的时间阈值重复寻找的过程。每当找到了能 够满足条件的连续Segment,系统就会触发一次自动合并Segment的构建 任务,在构建任务完成之后,新的Segment被设置为READY状态,自动合
并的整套尝试又需要重新再来一遍。

Cuboid剪枝优化

在Web GUI的Model页面选择一个READY状态的Cube,当我们把光标 移到该Cube的Cube Size列时,Web GUI会提示Cube的源数据大小,以及当 前Cube的大小除以源数据大小的比例,称为膨胀率(Expansion Rate),如 图6-2所示。
图6-2 查看Cube的膨胀率
一般来说,Cube的膨胀率应该在0%~1000%之间,如果一个Cube的膨 胀率超过1000%,那么Cube管理员应当开始挖掘其中的原因。通常,膨胀
率高有以下几个方面的原因。
·Cube中的维度数量较多,且没有进行很好的Cuboid剪枝优化,导致 Cuboid数量极多。
·Cube中存在较高基数的维度,导致包含这类维度的每一个Cuboid占
用的空间都很大,这些Cuboid累积造成整体Cube体积变大。
·存在比较占用空间的度量,例如Count Distinct,因此需要在Cuboid的 每一行中都为其保存一个较大的寄存器,最坏的情况将会导致Cuboid中 每一行都有数十KB,从而造成整个Cube的体积变大。
……
因此,对于Cube膨胀率居高不下的情况,管理员需要结合实际数据 进行分析,可灵活地运用本章接下来介绍的优化方法对Cube进行优化。

空间与时间的平衡

理论上所有能用Cuboid处理的查询请求都可以使用Base Cuboid来处 理,就好像所有能用Base Cuboid处理的查询请求都能够通过直接读取源 数据的方式来处理一样。但是Kylin之所以在Cube中物化这么多的 Cuboid,就是因为不同的Cuboid有各自擅长的查询场景。面对一个特定的 查询,使用精确匹配的Cuboid就好像是走了一条捷径,能帮助Kylin最快 地返回查询结果,因为这个精确匹配的Cuboid已经为此查询做了最大努
力的预先聚合,查询引擎中只需要做很少的运行时聚合就能返回结果。 每个Cuboid其实都代表着一种查询的样式,如果每种样式都要做好精确
的匹配,则会变得很奢侈,那么我们有必要考虑牺牲一部分查询样式的 精确匹配Cuboid。这个不精确匹配的Cuboid可能是6.1.2节中提到的 Cuboid的父亲Cuboid,甚至如果它的父亲Cuboid也被牺牲了,Kylin可能会 一路追溯到Base Cuboid来回答查询请求。使用不精确匹配的Cuboid比起 使用精确匹配的Cuboid,需要做更多查询时的聚合计算;但是如果Cube优
化得当,那么查询时的聚合计算的开销就会没有想象中的那么恐怖。以 6.1.2节中Shrink值接近100%的Cuboid为例,假设我们牺牲了这样的 Cuboid,那么只要它的父亲Cuboid被物化,使用它的父亲Cuboid的开销就 没那么大,因为父亲Cuboid并没有比它多很多行的记录。
从以上角度来说,Kylin的核心优势在于使用额外的空间存储预计算
的结果,以换取查询时间的缩减。而Cube的剪枝优化则是一种试图减少
额外空间占用的方法,这种方法的前提是不会明显影响查询时间的缩 减。在做剪枝优化的时候,需要选择跳过那些“多余”的Cuboid:有的 Cuboid因为查询样式的原因永远不会被查询到,因此显得多余;有的 Cuboid的能力和其他Cuboid接近,因此显得多余。但是Cube管理员无法提 前甄别每一个Cuboid是否多余,因此Kylin提供了一系列简单的工具来帮 助他们完成Cube的剪枝优化。

使用衍生维度

一个常见的时间维度表,里面充斥着用途各异的时间维度,例
如每个日期所处的星期、每个日期所处的月份等。这些维度可以被分析
师灵活地用来进行各个时间粒度上的聚合分析,而不需要进行额外的上 卷(Roll Up)操作。但是为了这个目的一下子引入这么多个维度,导致 Cube中总共的Cuboid数量呈现爆炸式的增长往往是得不偿失的,所以在
维度中只放入了这个维度表的主键(在底层实现中,我们更偏向使用事实 表上的外键,因为在left joint的情况下事实表外键是维度表主键的超集), 也就是只物化按日聚合的Cuboid。当用户需要以更高的粒度(比如按周、 按月)来聚合时,如果在查询时获取按日聚合的Cuboid数据,并在查询引
擎中实时地进行上卷操作,那么就达到了使用牺牲一部分运行时性能来 节省Cube空间占用的目的。
Kylin将这样的理念包装成为了一个简单的优化工具——衍生维度。
衍生维度用于在有效维度内将维度表上的非主键维度排除掉,并使用维 度表的主键(其实是事实表上相应的外键)来替代它们。Kylin会在底层记
录维度表主键与维度表其他维度之间的映射关系,以便在查询时能够动 态地将维度表的主键“翻译”成这些非主键维度,并进行实时聚合。

在创建Cube的Cube designer 的第二步,即添加维度的时候,要单击“Add Dimension”中的Derived而非 Normal。系统会弹出新的对话框让用户填写这批衍生维度的细节,一批衍生
维度对应一个需要做衍生维度处理的维度表。在这里首先为本批衍生维 度命名,例如“derived_date”,然后在下面选择相应的维度表,并且选择维 度表上的非主键维度(如图6-5所示)。如果维度表上的某个非主键维度没 有通过“+New Derived”选中,那么在查询时就不能包含它,因为系统不会 保存维度表主键(CAL_DT)与这个维度之间的映射关系。例如,假设没有 选择QTR_BEG_DT,那么带有QTR_BEG_DT的查询就会失败。
选中的衍生维度在Cube中并没有直接存在,事实上如果我们前往 Cube Designer的Advanced Setting,在Aggregation Groups和Rowkeys部分完
全看不到这些衍生维度,我们甚至还会找不到这个维度表 KYLIN_CAL_DT的主键,因为就如之前所描述的,我们实际上都是在用
事实表上的外键作为这些衍生维度背后真正有效的维度,在前面的例子 中,事实表与KYLIN_CAL_DT是通过以下的方式来连接的:
因此在Advanced Setting的Rowkeys部分就会看到PART_DT而看不到 CAL_DT,更看不到那些KYLIN_CAL_DT上的衍生维度

虽然衍生维度具有非常大的吸引力,但这也并不是说所有维度表上
的维度都得变成衍生维度,如果从维度表主键到某个维度表维度所需要 的聚合工作量非常大,例如从CAT_DT到YEAR_BEG_DT基本上需要 365∶1的聚合量,那么将YERR_BEG_DT作为一个普通的维度,而不是衍
生维度可能是一种更好的选择。

使用聚合组

聚合组(Aggregation Group)是一种更为强大的剪枝工具。聚合组假设 一个Cube的所有维度均可以根据业务需求划分成若干组(当然也可以是
一个组),由于同一个组内的维度更可能同时被同一个查询用到,因此会 表现出更加紧密的内在关联。每个分组的维度集合均是Cube所有维度的
一个子集,不同的分组各自拥有一套维度集合,它们可能与其他分组有
相同的维度,也可能没有相同的维度。每个分组各自独立地根据自身的 规则贡献出一批需要被物化的Cuboid,所有分组贡献的Cuboid的并集就 成为了当前Cube中所有需要物化的Cuboid的集合。不同的分组有可能会 贡献出相同的Cuboid,构建引擎会察觉到这点,并且保证每一个Cuboid无 论在多少个分组中出现,它都只会被物化一次。
对于每个分组内部的维度,用户可以使用如下三种可选的方式定义
它们之间的关系,具体如下。
·强制维度(Mandatory),如果一个维度被定义为强制维度,那么这个
分组产生的所有Cuboid中每一个Cuboid都会包含该维度。每个分组中都 可以有0个、1个或多个强制维度。如果根据这个分组的业务逻辑,则相关
的查询一定会在过滤条件或分组条件中,因此可以在该分组中把该维度
设置为强制维度。
·层级维度(Hierarchy),每个层级包含两个或更多个维度。假设一个 层级中包含D1,D2…Dn这n个维度,那么在该分组产生的任何Cuboid中, 这n个维度只会以(),(D1),(D1,D2)…(D1,D2…Dn)这n+1种形式中的 一种出现。每个分组中可以有0个、1个或多个层级,不同的层级之间不应
当有共享的维度。如果根据这个分组的业务逻辑,则多个维度直接存在
层级关系,因此可以在该分组中把这些维度设置为层级维度。
·联合维度(Joint),每个联合中包含两个或更多个维度,如果某些列 形成一个联合,那么在该分组产生的任何Cuboid中,这些联合维度要么一 起出现,要么都不出现。每个分组中可以有0个或多个联合,但是不同的
联合之间不应当有共享的维度(否则它们可以合并成一个联合)。如果根
据这个分组的业务逻辑,多个维度在查询中总是同时出现,则可以在该
分组中把这些维度设置为联合维度。
,单击左下角的“New Aggregation Group”可以添加一个新的分组。在某一个分组内,我们首先 需要制定这个分组包含(Include)哪些维度,然后就可以进行强制维度、层 级维度和联合维度的创建。除了Include选项,其他的三项都是可选的。
聚合组的设计非常灵活,甚至可以用来描述一些极端的设计。假设 我们的业务需求非常单一,只需要某些特定的Cuboid,那么可以创建多个 聚合组,每个聚合组代表一个Cuboid。具体的方法是在聚合组中先包含某 个Cuboid所需的所有维度,然后把这些维度都设置为强制维度。这样当前 的聚合组就只能产生我们想要的那一个Cuboid了。
再比如,有的时候我们的Cube中有一些基数非常大的维度,如果不
做特殊处理,它就会和其他的维度进行各种组合,从而产生一大堆包含 它的Cuboid。包含高基数维度的Cuboid在行数和体积上往往非常庞大,这
会导致整个Cube的膨胀率变大。如果根据业务需求知道这个高基数的维
度只会与若干个维度(而不是所有维度)同时被查询到,那么就可以通过 聚合组对这个高基数维度做一定的“隔离”。我们把这个高基数的维度放
入一个单独的聚合组,再把所有可能会与这个高基数维度一起被查询到 的其他维度也放进来。这样,这个高基数的维度就被“隔离”在一个聚合组
中了,所有不会与它一起被查询到的维度都没有和它一起出现在任何一 个分组中,因此也就不会有多余的Cuboid产生。这点也大大减少了包含该 高基数维度的Cuboid的数量,可以有效地控制Cube的膨胀率。


未完待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值