【读书笔记】数据仓库- Apache Kylin权威指南

Apache Kylin权威指南(第2版)

◆ 1.2.1 为什么要使用Apache Kylin

它们的主要技术是“大规模并行处理”(Massively Parallel Processing,MPP)和“列式存储”(Columnar Storage)

◆ 1.2.2 Apache Kylin怎样解决关键问题

基于以上两点,我们得到一个新的思路——“预计算”。应尽量多地预先计算聚合结果,在查询时刻也尽量使用预计算的结果得出查询结果,从而避免直接扫描可能无限增长的原始记录

“预计算”就是Kylin在“大规模并行处理”和“列式存储”之外,提供给大数据分析的第三个关键技术。

◆ 1.3.1 维度和度量简介

维度就是观察数据的角度。比如电商的销售数据,可以从时间的维度来观察(如图1-2的左图所示),也可以进一步细化从时间和地区的维度来观察(如图1-2的右图所示)。维度一般是一组离散的值,比如时间维度上的每一个独立的日期,或者商品维度上的每一件独立的商品。因此,统计时可以把维度值相同的记录聚合起来,应用聚合函数做累加、平均、去重复计数等聚合计算。

度量就是被聚合的统计值,也是聚合运算的结果,它一般是连续值,如图1-2中的销售额,抑或是销售商品的总件数。通过比较和测算度量,分析师可以对数据进行评估,比如今年的销售额相比去年有多大的增长、增长的速度是否达到预期、不同商品类别的增长比例是否合理等。

◆ 1.3.2 Cube和Cuboid

对每一种维度的组合,将度量做聚合运算,运算的结果保存为一个物化视图,称为Cuboid。将所有维度组合的Cuboid作为一个整体,被称为Cube。所以简单来说,一个Cube就是许多按维度聚合的物化视图的集合。

将计算的结果保存为物化视图,所有Cuboid物化视图的总称就是Cube了。

◆ 1.3.3 工作原理

Apache Kylin的工作原理就是对数据模型做Cube预计算,并利用计算的结果加速查询。过程如下:
(1)指定数据模型,定义维度和度量。
(2)预计算Cube,计算所有Cuboid并将其保存为物化视图。
(3)执行查询时,读取Cuboid,进行加工运算产生查询结果。

◆ 1.4 Apache Kylin的技术架构

根据元数据定义,下方构建引擎从数据源中抽取数据,并构建Cube。数据以关系表的形式输入,且必须符合星形模型(Star Schema)或雪花模型(Snowflake Schema)。用户可以选择使用MapReduce或Spark进行构建。构建后的Cube保存在右侧的存储引擎中,目前HBase是默认的存储引擎。

查询引擎解析SQL,生成基于关系表的逻辑执行计划,然后将其转译为基于Cube的物理执行计划,最后查询预计算生成的Cube产生结果。整个过程不访问原始数据源。

◆ 1.5 Apache Kylin的主要特点

Kylin的主要特点包括支持SQL接口、支持超大数据集、秒级响应、可伸缩性、高吞吐率、BI及可视化工具集成等。

◆ 1.6 与其他开源产品的比较

Kylin的特色在于,在上述底层技术之外,另辟蹊径地使用了独特的Cube预计算技术。预计算事先将数据按维度组合进行了聚合,将结果保存为物化视图。经过聚合,物化视图的规模就只由维度的基数决定,而不再随数据量的增加呈线性增长。以电商为例,如果业务扩张,交易量增加了10倍,只要交易数据的维度不变(供应商/商品种类数量不变),聚合后的物化视图依旧是原先的大小,查询的速度也将保持不变。

◆ 2.1.1 数据仓库、OLAP与BI

OLAP(Online Analytical Process),即联机分析处理,它可以以多维度的方式分析数据,并且能弹性地提供上卷(Roll-up)、下钻(Drill-down)和透视分析(Pivot)等操作,是呈现集成性决策信息的方法,其主要功能在于方便大规模数据分析及统计计算,多用于决策支持系统、商务智能或数据仓库。

BI(Business Intelligence),即商务智能,是指用现代数据仓库技术、在线分析技术、数据挖掘和数据展现技术进行数据分析以实现商业价值。

◆ 2.1.3 事实表和维度表

事实表(Fact Table)是指存储事实记录的表,如系统日志、销售记录等,并且是维度模型中的主表,代表着键和度量的集合。事实表的记录会不断地动态增长,所以它的体积通常远大于其他表,通常事实表占据数据仓库中90%或更多的空间

维度表(Dimension Table),也称维表或查找表(Lookup Table),是与事实表相对应的一种表。维度表的目的是将业务含义和上下文添加到数据仓库中的事实表和度量中。维度表是事实表的入口点,维度表实现了数据仓库的业务接口。它们基本上是事实表中的键引用的查找表。它保存了维度的属性值,可以与事实表做关联,相当于将事实表上经常出现的属性抽取、规范出来用一张表进行管理

◆ 2.1.4 维度和度量

维度是人们观察数据的特定角度,是考虑问题时的一类属性。它通常是数据记录的一个特征,如时间、地点等。同时,维度具有层级概念,可能存在细节程度不同的描述方面,如日期、月份、季度、年等。

在数据仓库中,可以在数学上求和的事实属性称为度量。

◆ 2.1.5 Cube、Cuboid和Cube Segment

Cube(或称Data Cube),即数据立方体,是一种常用于数据分析与索引的技术,它可以对原始数据建立多维度索引,大大加快数据的查询效率。Cuboid特指Apache Kylin中在某一种维度组合下所计算的数据。Cube Segment指针对源数据中的某一片段计算出来的Cube数据。通常,数据仓库中的数据数量会随时间的增长而增长,而Cube Segment也是按时间顺序构建的。

◆ 2.2 在Hive中准备数据

Hive可以将结构化的数据文件映射为数据库表,并可以将SQL语句转换为MapReduce或Tez任务运行,从而让用户以类SQL(HiveQL,HQL)的方式管理和查询Hadoop上的海量数据。

◆ 2.2.3 Hive表分区

Hive表支持多分区(partition)。简单来说,一个分区就是一个文件目录,存储了特定的数据文件。当有新的数据生成的时候,可以将数据加载到指定的分区,读取数据的时候也可以指定分区。对于SQL查询,如果查询中指定了分区列的属性条件,则Hive会智能地选择特定分区(目录),从而避免全量数据的扫描,减少读写操作对集群的压力。

◆ 2.2.4 了解维度的基数

维度的基数(Cardinality)指的是该维度在数据集中出现的不同值的个数。

基数超过一百万的维度通常被称为超高基数维度(UltraHigh Cardinality,UHC)

Cube中所有维度的基数可以体现Cube的复杂度,如果一个Cube中有多个超高基数维度,那么这个Cube膨胀的几率就会很高。

◆ 2.4.2 创建数据模型

添加维度表的时候,首先选择表之间的连接关系,同时选择表之间的连接类型:是inner jion还是leftjion,并为创建的维度表输入别名。同时可以选择是否将其以快照(Snapshot)形式存储到内存中以供查询。当维度表小于300MB时,推荐启用维度表以快照形式存储,以简化Cube计算和提高系统整体效率。当维度表超过300MB上限时,则建议关闭维度表快照,以提升Cube构建的稳定性与查询的性能。

选择维度列时,维度可以来自事实表或维度表

过滤(Filter)条件是指,如果想把一些记录忽略掉,那么这里可以设置一个过滤条件。ApacheKylin在向Hive请求源数据的时候,会带上此过滤条件。

◆ 2.4.3 创建Cube

如果被设定为衍生维度的话,由于这些列值都可以从该维度表的主键值中衍生出来,所以实际上只有主键列会被Cube加入计算。而在Apache Kylin的具体实现中,往往采用事实表上的外键替代主键进行计算和存储。但是逻辑上可以认为衍生列来自维度表的主键。

默认Apache Kylin会把所有维度放在同一个聚合组(Aggregation Group,也称维度组)中,如果维度数较多(如>15),建议用户根据查询的习惯和模式,点击“New Aggregation Group+”命令,将维度分布到多个聚合组中。通过使用多个聚合组,可以大大降低Cube中的Cuboid数量。

层级维度(Hierarchy Dimensions):是指一组有层级关系的维度,如“国家”“省”“市”,这里“国家”是高级别的维度,“省”“市”依次是低级别的维度。用户会按高级别维度进行查询,也会按低级别维度进行查询,但当查询低级别维度时,往往会带上高级别维度的条件,而不会孤立地审视低维度的数据。

在Apache Kylin中是以Key-Value的形式将Cube的构建结果存储到Apache HBase中的

HBase的Rowkey,即行键是用来检索其他列的唯一索引。Apache Kylin需要按照多个维度来对度量进行检索,因此在存储到HBase的时候,需要将多个维度值进行拼接组成Rowkey。

由于同一维度中的数值长短不一,如国家名,短的如“中国”,长的如“巴布亚新几内亚”,因此将多个不同列的值进行拼接的时候,要么添加分隔符,要么通过某种编码使各个列所占的宽度固定。Apache Kylin为了能够在HBase上高效地进行存储和检索,会使用第二种方式对维度值进行编码。

编码(Encoding)代表了该维度的值使用何种方式进行编码,默认采用字典(Dictionary)编码技术。而合适的编码能够减少维度对空间的占用。

字典编码是将所有此维度下的值构建成一张映射表,从而大大节约存储空间。另外,字典是保持顺序的,这样可以使得在HBase中进行比较查询的时候,依然使用编码后的值,而无须解码。Dictionary的优势是,产生的编码非常紧凑,尤其在维度的值基数小且长度大的情况下,Dictionary编码特别节约空间。由于产生的字典在使用时加载进构建引擎和查询引擎,所以在维度的基数大、长度也大的情况下,容易造成构建引擎或者查询引擎的内存溢出。

Global Dictionary可以将一个非integer的值转成integer值,以便bitmap进行去重,如果你要计算COUNT DISTINCT的列本身已经是integer类型,那就不需要定义Global Dictionary。并且Global Dictionary会被所有segment共享,因此支持跨segments做上卷去重操作。

Segment Dictionary虽然也是用于精确计算COUNT DISTINCT的字典,但与Global Dictionary不同的是,它是基于一个segment的值构建的,因此不支持跨segments的汇总计算。如果你的cube不是分区的或者能保证你的所有SQL按照partition column进行group by,那么最好使用SegmentDictionary而不是Global Dictionary,这样可以避免单个字典过大的问题。

Kylin目前提供的Cube构建引擎有两种:MapReduce和Spark(如图2-20所示)。如果你的Cube只有简单度量(如SUM、MIN、MAX),建议使用Spark。如果Cube中有复杂类型度量(如COUNT DISTINCT、TOP_N),建议使用MapReduce。

◆ 2.5 构建Cube

新创建的Cube只有定义,而没有计算的数据,它的状态是“DISABLED”,是不会被查询引擎挑中的。要想让Cube有数据,还需对它进行构建。Cube的构建方式通常有两种:全量构建和增量构建,两者的构建步骤是完全一样的,区别只在于构建时读取的数据源是全集还是子集。

◆ 2.5.1 全量构建和增量构建

对数据模型中没有指定分割时间列信息的Cube,Apache Kylin会采用全量构建,即每次都从Hive中读取全部的数据来开始构建。

进行增量构建的时候,Apache Kylin每次都会从Hive中读取一个时间范围内的数据,然后对其进行计算,并以一个Segment的形式保存。下次构建的时候,自动以上次结束的时间为起点时间,再选择新的终止时间进行构建。经过多次构建后,Cube中会有多个Segment依次按时间顺序进行排列,如Seg-1,Seg-2,…,Seg-N。进行查询的时候,Apache Kylin会查询一个或多个Segment然后再做聚合计算,以便返回正确的结果给请求者。

◆ 2.5.2 历史数据刷新

Cube构建完成以后,如果某些历史数据发生了变动,需要针对相应的Segment重新进行计算,这种构建称为刷新。刷新通常只针对增量构建的Cube而言,因为全量构建的Cube只要重新全部构建就可以得到更新;而增量更新的Cube因为有多个Segment,需要先选择要刷新的Segment,然后再进行刷新。

在刷新的同时,Cube仍然可以被查询,只是返回的是陈旧数据。当Segment刷新完毕后,新Segment会立即生效,查询开始返回最新的数据。原Segment则成为垃圾,等待回收。

◆ 2.5.3 合并

合并的时候,Apache Kylin会直接以最初各个Segment构建时生成的Cuboid文件作为输入内容,不需要从Hive中加载原始数据。后续的步骤跟构建时基本一致。直到新的HTable加载完成,Apache Kylin才会卸载原来的HTable,以确保在整个合并过程中,Cube都是可以查询的。

◆ 2.6 查询Cube

只有当查询的模式跟Cube定义相匹配的时候,Apache Kylin才能够使用Cube的数据来完成查询。“Group By”的列和“Where”条件里的列,必须是在维度中定义的列,而SQL中的度量,应该跟Cube中定义的度量一致。

◆ 2.6.1 Apache Kylin查询介绍

Kylin使用Apache Calcite做SQL语法分析,并且Apache Kylin深度定制了Calcite。ApacheCalcite是一个开源的SQL引擎,它提供了标准SQL语言解析、多种查询优化和连接各种数据源的能力。Calcite项目在Hadoop中越来越引人注目,并被众多项目集成为SQL解析器。

Kylin一条查询的执行过程主要分成四个部分:词法分析、逻辑执行计划、物理执行计划和执行。

Kylin则在其中增加了一些优化策略。首先,在每一个优化规则中将对应的物理算子转换成Apache Kylin自己的OLAPxxxRel算子。然后根据每一个算子中保持的信息构造本次查询的上下文OLAPContext。之后再根据本次查询中使用的维度列、度量信息等查询是否有满足本次查询的Cuboid,如果有则将其保存在OLAPContext的realization中

◆ 2.6.2 查询下压

Kylin中的查询,只有预先针对查询内的维度和度量进行建模并构建Cube才能够回答查询。因此在Apache Kylin中针对于无法击中Cube的查询,便有了另外一种处理方式即查询下压。查询下压的本质是将无法用Cube回答的查询路由到Hive或Spark这类查询引擎,用以回答该查询。

一条查询经过解析后,进入查询路由,首先会进入Cube查询执行器中去寻找是否有能够回答该查询的Cube。如果没有找到合适的Cube,则会抛出异常“No realization found.”,并将这个结果抛回查询路由,查询路由检测到该异常后,则会将该查询路由到一个外部查询引擎(如Hive),以回答这条查询。

◆ 2.7 SQL参考

Kylin作为OLAP引擎,只支持查询,而不支持其他操作,如插入、更新等,即所有SQL都必须是SELECT语句,否则Apache Kylin会报错。

◆ 第3章 Cube优化

相比普通的大规模并行处理解决方案,Kylin具有响应时间快、查询时资源需求小、吞吐量大等优点。用户的数据模型包括维度、度量、分区列等基本信息,也包括用户通过Cube优化工具赋予其的额外的模型信息。

在构建Cube之前,Cube的优化手段提供了更多与数据模型或查询样式相关的信息,用于指导构建出体积更小、查询速度更快的Cube。可以看到Cube的优化目标始终有两个大方向:空间优化和查询时间优化。

◆ 3.1 Cuboid剪枝优化

在没有采取任何优化措施的情况下,Kylin会对每一种维度的组合进行聚合预计算,维度的一种排列组合的预计算结果称为一个Cuboid。

最底端的包含所有维度的Cuboid称为Base Cuboid,它是生成其他Cuboid的基础

假设用户有10个维度,那么没做任何优化的Cube总共会存在210=1024个Cuboid,而如果用户有20个维度,那么Cube中总共会存在220=1048576个Cuboid!虽然每个Cuboid的大小存在很大差异,但是仅Cuboid的数量就足以让人意识到这样的Cube对构建引擎、存储引擎来说会形成巨大的压力。因此,在构建维度数量较多的Cube时,尤其要注意进行Cube的剪枝优化。

◆ 3.1.2 检查Cuboid数量

Kylin提供了一种简单的工具供用户检查Cube中哪些Cuboid最终被预计算了,将其称为被物化(materialized)的Cuboid。同时,这种工具还能给出每个Cuboid所占空间的估计值。该工具需要在Cube构建任务对数据进行一定的处理之后才能估算Cuboid的大小,具体来说,就是在构建任务完成“Save Cuboid Statistics”这一步骤后才可以使用该工具。

Segment的大小估算是构建引擎自身用来指导后续子步骤的,如决定mapper和reducer数量、数据分片数量等的依据,虽然有的时候对Cuboid的大小的估计存在误差(因为存储引擎对最后的Cube数据进行了编码或压缩,所以无法精确预估数据大小),但是整体来说,对于不同Cuboid的大小估计可以给出一个比较直观的判断。由于没有编码或压缩时的不确定性因素,因此Segment中的行数估计会比大小估计来得更加精确一些。

所有的Cuboid及其分析结果以树状的形式打印了出来。在这棵树中,每个节点代表一个Cuboid,每个Cuboid的ID都由一连串1或0的数字组成,数字串的长度等于有效维度的数量,从左到右的每个数字依次代表Cube的Rowkeys设置中的各个维度。如果数字为0,则代表这个Cuboid中不存在相应的维度,如果数字为1,则代表这个Cuboid中存在相应的维度。

除了最顶端的Cuboid之外,每个Cuboid都有一个父Cuboid,且都比父Cuboid少了一个“1”。其意义是这个Cuboid是由它的父节点减少一个维度聚合得来的(上卷,即roll up操作)。最顶端的Cuboid称为Base Cuboid,它直接由源数据计算而来。Base Cuboid中包含了所有的维度,因此它的数字串中所有的数字均为1。

◆ 3.1.3 检查Cube大小

当前Cube的大小与源数据大小的比例,称之为膨胀率(ExpansionRate)

◆ 3.1.4 空间与时间的平衡

理论上所有能用Cuboid处理的查询请求,都可以使用Base Cuboid来处理,就好像所有能用BaseCuboid处理的查询请求都能够通过直接读取源数据的方式来处理一样。但是Kylin之所以在Cube中物化这么多的Cuboid,就是因为不同的Cuboid有各自擅长的查询场景。

每个Cuboid在技术上代表着一种维度的排列组合,在业务上代表着一种查询的样式;为每种查询样式都做好精确匹配是理想状态,但那会导致很高的膨胀率,进而导致很长的构建时间。所以在实际的Cube设计中,我们会考虑牺牲一部分查询样式的精确匹配,让它们使用不是完全精确匹配的Cuboid,在查询进行时再进行后聚合。

使用不精确匹配的Cuboid比起使用精确匹配的Cuboid需要做更多查询时的后聚合计算

Kylin的核心优势在于使用额外的空间存储预计算的结果,来换取查询时间的缩减。而Cube的剪枝优化,则是一种试图减少额外空间的方法,使用这种方法的前提是不会明显影响查询时间的缩减。

◆ 3.2 剪枝优化工具

衍生维度。将一个维度表上的维度设置为衍生维度,则这个维度不会参与预计算,而是使用维度表的主键(其实是事实表上相应的外键)来替代它。Kylin会在底层记录维度表主键与维度表其他维度之间的映射关系,以便在查询时能够动态地将维度表的主键“翻译”成这些非主键维度,并进行实时聚合。

◆ 3.2.2 聚合组

聚合组(Aggregation Group)是一个强大的剪枝工具,可以在Cube Designer的AdvancedSettings里设置不同的聚合组。聚合组将一个Cube的所有维度根据业务需求划分成若干组(当然也可以只有一个组),同一个组内的维度更可能同时被同一个查询用到,因此表现出更加紧密的内在关联。不同组之间的维度在绝大多数业务场景里不会用在同一个查询里,因此只有在很少的Cuboid里它们才有联系。所以如果一个查询需要同时使用两个聚合组里的维度,一般从一个较大的Cuboid在线聚合得到结果,这通常也意味着整个查询会耗时较长。

◆ 3.2.5 联合维度

联合维度(Joint Dimension)一般用在同时查询几个维度的场景,它是一个比较强力的维度剪枝工具,往往能把Cuboid的总数降低几个数量级。

联合维度的适用场景:●维度经常同时在查询where或group by条件中同时出现,甚至本来就是一一对应的,如customer_id和customer_name,将它们组成一个联合维度。●将若干个低基数(建议每个维度基数不超过10,总的基数叉乘结果小于10000)的维度合并组成一个了联合维度,可以大大减少Cuboid的数量,利用在线计算能力,虽然会在查询时多耗费有限的时间,但相比能减少的存储空间和构建时间而言是值得的。●必要时可以将两个有强关系的高基维度组成一个联合维度,如合同日期和入账日期。●可以将查询时很少使用的若干维度组成一个联合维度,在少数查询场景中承受在线计算的额外时间消耗,但能大大减少存储空间和构建时间。

◆ 3.3 并发粒度优化

当Segment中的某一个Cuboid的大小超出一定阈值时,系统会将该Cuboid的数据分片到多个分区中,以实现Cuboid数据读取的并行化,从而优化Cube的查询速度。

构建引擎根据Segment估计的大小,以及参数“kylin.hbase.region.cut”的设置决定Segment在存储引擎中总共需要几个分区来存储,如果存储引擎是HBase,那么分区数量就对应HBase中的Region的数量。kylin.hbase.region.cut的默认值是5.0,单位是吉字节(GB),也就是说,对于一个大小估计是50GB的Segment,构建引擎会给它分配10个分区。

◆ 3.4 Rowkey优化

Cube的每个Cuboid中都包含大量的行,每个行又分为Rowkey和Measure两个部分。每行Cuboid数据中的Rowkey都包含当前Cuboid中所有维度的值的组合。Rowkey中的各个维度按照CubeDesigner→Advanced Setting→RowKeys中设置的顺序和编码进行组织

◆ 3.4.2 选择合适的维度编码

字典(Dictionary)编码(默认的编码)不适用于高基数维度(基数值在300万以上)

Fixed_length编码是最简单的编码,它通过补上空字符(如果维度值长度小于指定长度))或截断(如果维度值长度大于指定长度),从而将所有值都变成等长,然后拼接到Rowkey中。

◆ 3.4.3 按维度分片

默认情况下,Cuboid的分片策略是对于所有列进行哈希计算后随机分配的。也就是说,我们无法控制Cuboid的哪些行会被分到同一个分片中。这种默认的方法固然能够提高读取的并发程度,但是它仍然有优化的空间。按维度分片提供了一种更加高效的分片策略,那就是按照某个特定维度进行分片(Shard By Dimension)。简单地说,当你选取了一个维度用于分片后,如果Cuboid中的某两行在该维度上的值相同,那么无论这个Cuboid最终被划分成多少个分片,这两行数据必然会被分配到同一个分片中。

Cuboid的每个分片会被分配到存储引擎的不同物理机器上。Kylin在读取Cuboid数据的时候会向存储引擎的若干机器发送读取的RPC请求。在RPC请求接收端,存储引擎会读取本机的分片数据,并在进行一定的预处理后发送RPC回应(如图3-14所示)。以HBase存储引擎为例,不同的Region代表不同的Cuboid分片,在读取Cuboid数据的时候,HBase会为每个Region开启一个Coprocessor实例来处理查询引擎的请求。查询引擎将查询条件和分组条件作为请求参数的一部分发送到Coprocessor中,Coprocessor就能够在返回结果之前对当前分片的数据做一定的预聚合

◆ 3.5 Top_N度量优化

Kylin v1.5以后的版本中引入了Top_N度量,意在进行Cube构建的时候预计算好需要的Top_N,在查询阶段就可以迅速地获取并返回Top_N记录。这样,查询性能就远远高于没有Top_N预计算结果的Cube,方便分析师对这类数据进行查询。

◆ 3.6 Cube Planner优化

Kylin自v2.3.0以后的版本引入了Cube Planner功能,自动地对Cube的结构进行优化。如图3-16所示,在用户定义的Aggregation Group等手动优化基础上,Cube Planner能根据每个Cuboid的大小和它对整个Cube产生的查询增益,结合历史查询数据对Cuboid进行进一步剪枝。Cube Planner使用贪心算法和基因算法排除不重要和不必要的Cuboid,不对这些Cuboid进行预计算,从而大大减少计算量、节约存储空间,从而提高查询效率。

◆ 第4章 增量构建

Kylin中将Cube划分为多个Segment,Segment代表一段时间内源数据的预计算结果,每个Segment都用起始时间和结束时间标记。在大部分情况下(特殊情况见第7章“流式构建”),一个Segment的起始时间等于它前面的Segment的结束时间,同理,它的结束时间等于它后面的Segment的起始时间。同一个Cube下不同的Segment除了背后的源数据不同,其他如结构定义、构建过程、优化方法、存储方式等完全相同。

◆ 4.1 为什么要增量构建

整体来说,增量构建的Cube上的查询会比全量构建Cube上的查询做更多的运行时聚合,而这些运行时聚合都发生在单点的查询引擎上,因此,通常来说,增量构建的Cube上的查询会比全量构建的Cube上的查询慢一些。

◆ 4.2 设计增量Cube

并非所有的Cube都适合进行增量构建,Cube的定义必须包含一个时间维度,用来分割不同的Segment,我们将这样的维度称为分割时间列(Partition Date Column)。

◆ 4.2.2 增量Cube的创建

增量构建的Cube需要指定分割时间列。同一个Model下的不同分割时间列的定义应该是相同的,因此我们将分割时间列的定义放到了Model之中。

◆ 4.3 触发增量构建

Kylin在有该Cube构建任务运行的情况下,不再接受该Cube上新的构建任务。换言之,仅当Cube中不存在任何Segment,或者不存在任何未完成的构建任务时,Kylin才会接受该Cube上新的构建任务。未完成的构建任务不仅包含正在运行中的构建任务,还包括已经出错并处于ERROR状态的构建任务。如果存在ERROR状态的构建任务,用户需要先处理好该构建任务,才能成功地向Kylin提交新的构建任务。

◆ 5.1.1 查询

在输入框的右下角有一个“Limit”字段,用来保证Kylin不会返回超大结果集而拖垮浏览器(或其他客户端)。如果SQL中没有Limit子句,这里默认会拼接上Limit 50000,如果SQL中有Limit子句,那么以SQL中的设置为准。假如用户想去掉Limit限制,只需SQL中不加Limit的同时右下角的Limit输入框也改为0即可。

◆ 5.3 ODBC

Apache Kylin提供了32位和64位两种ODBC驱动,支持ODBC的应用可以基于该驱动访问Kylin。

◆ 第6章 Cube Planner及仪表盘

CubePlanner功能通过计算不同Cuboid的构建成本和收益,并结合用户查询的统计数据挑选出更精简、更高效的维度组合,从而减少构建Cube耗费的时间和空间,提高查询效率。

◆ 6.1.1 为什么要引入Cube Planner

Kylin会对每一种维度的组合进行预计算,每种维度组合的预计算结果被称为Cuboid,这些Cuboid组成了Cube。

◆ 6.1.2 Cube Planner算法介绍

Cuboid的成本主要包含如下两个方面。●构建成本:取决于该维度组合的数据行数。●查询成本:取决于查询该Cuboid需要扫描的行数。由于构建往往是一次性的,而查询是重复性的,因此这里忽略构建成本,只使用查询成本来计算。

贪心算法使用多轮迭代,每次选出当前状态下最优的一种维度组合并将其加入推荐列表。

基因算法的核心思想是交叉变异,优胜劣汰,主要步骤包括“选择”→“交叉”→“变异”。假设每个Cuboid都是一个基因,一种Cuboid组合是一个染色体,染色体用01字符串表示,其中1表示该Cuboid在这个Cube里出现,0表示不出现。

◆ 6.2 System Cube

Apache Kylin自v2.3.0版本引入了System Cube来收集运行时的各种指标数据。System Cube中存储着各种指标数据,通过这些指标我们可以了解每个查询服务器的使用情况,每个项目的查询总次数、查询失败率、查询时延等查询信息,配合Dashboard就可以实时掌握Kylin的查询服务情况。

◆ 6.4 小结

System Cube可将收集到的数据整理并存储起来,是用户对Cube进行管理和监控的一个非常有用的工具。仪表盘为指标数据提供了一个可视化的展示平台,能让用户更直观地了解Cube的使用历史状态。Cube Planner能够利用System Cube中收集的数据,帮助我们找到更重要的Cuboid,提高资源利用率和查询效率。同时,它也能降低设计Cube的门槛,帮助更多的人更轻松地使用Kylin。

◆ 第7章 流式构建

增量构建和全量构建一样,都需要从Hive中抽取数据,在经过若干轮的MapReduce或Spark作业后,才能对源数据进行预计算,最后将预计算得到的结果适配成存储引擎所需的格式,并导入存储引擎中。

通常,企业会使用像Apache Kafka这样的分布式消息队列来缓存流数据,随后使用Apache Storm、Apache Spark、Apache Flink等技术对流消息进行计算和处理。流处理技术被广泛应用在监控、推荐等场景中。如果Kylin能够直接对接和消费Kafka消息流,而不需要使用Hive,一方面可扩大Kylin的适用场景、简化ETL的过程;另一方面可以将数据从产生到被分析的延迟大大缩短。

在Kylin v1.5版本中,团队第一次尝试了对接Kafka消息流,并取得了一定成果。它的主要原理是使用独立的Java进程以定时微批次的方式对Kafka消息进行消费并将其加工成Cube。但是在长时间运行后,该设计暴露出了运维难的问题,如无法自动扩展以支持更大的消息流、无法保证晚到的消息被消费等

在Kylin v1.6版本中,团队针对v1.5版本中流式构建暴露出的问题进行了整改,甚至重新设计,以使Kylin的流数据源可以利用Hadoop进行处理,从而更易于扩展和运维;并且抽象了Kylin中Segment之间分区的概念,允许它们使用Kafka offset做分区,但在时间上可以有重叠,这样晚到的消息也会被构建

在后来的v2.4版本中,Kylin进一步支持Kafka消息(作为事实表)与Hive中的维度表进行join,丰富了它的使用场景

◆ 7.2.2 消息队列

流式构建的用户需要使用Kafka的Producer将数据源源不断地加入某个Topic中,并且将Kafka的一些基本信息(如Broker节点信息和Topic名称)告知流式构建任务。流式构建任务在启动时会启动Kafka客户端,然后根据配置向Kafka集群读取相应的Topic中的消息,并进行预处理计算。

◆ 7.3 设计流式Cube

和增量构建的流程一样,我们也要为流式构建的Cube创建数据模型(Model)。在Kylin v2.4之前的版本,对于流式数据源,模型只支持一张表,也就是所有字段都需要从流式表中来;在Kylin v2.4及以后的版本中,支持了流式数据表与Hive表进行join,也就是说可以用Hive中的维度表,流式表中只要有外键就可以了,这提升了流式数据分析的灵活性。但请注意,目前Kylin不支持多个流式数据表进行join。

◆ 7.4 流式构建原理

Kylin v1.6版本引入的流式构建是基于Kylin v1.5版本的可插拔架构而实现的,在此架构中,Kylin的构建引擎、查询引擎与数据源之间通过抽象接口来完成数据调用,数据源对于构建和存储是透明的。这样的设计可以大大降低增加一个数据源的难度:开发者只需要实现特定接口即可,后续构建过程可以重用已有的代码和逻辑。这样的架构对于维护Kylin这样一个多依赖的系统来说尤为重要,否则每增加一个构建引擎或查询引擎,每个数据源都要改动一番,尤其痛苦。

Kylin对于Hive表的构建,会启动一个hive命令,创建一张临时外表,然后将需要的字段和时间范围的数据,抽取到特定的HDFS目录下。同样地,对于Kafka数据源,Kylin也可以启动一个在YARN上的任务,将Kafka topic中特定范围的数据以期望的格式(默认为sequence文件格式)写到特定的HDFS目录中。考虑到Kafka topic通常分为多个分区(partition),因此Kylin可以启动多个mapper,每个mapper消费一个partition上对应的消息区间,并行地读取数据,从而加快处理速度

在Kafka中,虽然消息不是严格按时间顺序增长的,但是每个消息都有唯一的offset值(long类型),这个值是消费消息的主要索引,是只增且不可修改的

为了简单,把Kafka消息写到HDFS后,Kylin将它描述成一张临时的Hive表,然后使用HiveQL将它与同在Hive中的维度表进行join操作,类似于对普通Hive数据源的处理。

准实时的流式构建就实现了利用Hadoop进行并行处理,同时也实现了消息的不重不丢,以及和Hive表的连接。此外,其他特性也在流式场景下得到了支持,如Spark的构建引擎、未来的Parquet列式存储等。对于系统管理员来说,几乎不用区分Hive数据源和Kafka数据源,它们的区别只体现在构建频率上。

◆ 7.5 触发流式构建

将Kafka topic用作数据源,第一次触发Cube构建的时候,Kylin会自动从Kafka集群获取该topic最早的offset和当下最新的offset,随后启动任务消费这个范围内的数据。当触发下次构建时,Kylin会从上次构建的结束位置开始,消费到当下的最新offset。因此,用户几乎不用关心构建的范围,就仿佛点击一个按钮,告诉Kylin“请帮我把截至目前的新消息构建进Cube”一样。用户只需要掌握触发构建的时间点即可,如是每1小时构建一次,还是每15分钟构建一次。此外,构建的频率也是可以随时调整的,如在白天业务人员需要及时看到数据的时候,每15分钟构建一次,在夜晚和周末的时候降至小时级别。

◆ 7.5.2 自动化多次触发

随着5分钟构建一次的Segment不断堆积,自动合并也会被触发,Kylin会使用增量构建中的合并构建把小Segment陆续合并成大Segment,以保证查询性能。对于合并操作,Kylin使用与增量构建中相同的方式进行合并,不再依赖于特殊的流式构建引擎。

◆ 7.5.5 出错处理

如果自动合并构建出错,会导致新产生的Segment迅速堆积,Cube的查询性能也会下降。因此,每当合并构建出错时,管理员需要及时查看合并失败的原因,排除故障并在Web GUI中恢复该合并构建。合并构建的失败往往与流式构建本身没有直接的关系,因为合并不是流式构建引擎的专有功能。出错的原因往往和增量构建一样,问题出在Hadoop集群本身。

◆ 7.6 小结

目前的流式构建基于增量构建的整体框架,流式构建和增量构建大体相同,主要区别之处在于数据源不同,前者的数据源是Kafka这样的消息队列,而后者的数据源是Hive这样的数据仓库。尽管使用Hadoop MapReduce构建流式数据在效率上未必最佳,但是胜在稳定性和可靠性上。如果对Kylin了解比较多,可以将In-mem cubing或Spark cubing应用到流场景中,进一步加快数据的处理速度。在一些典型场景中,可以做到每10分钟构建一次,单次构建时间控制在3分钟左右,已能满足很多实时性要求中等的场景。

◆ 第8章 使用Spark

目前无论是全量构建、增量构建还是流式构建,默认的计算引擎都是MapReduce,但是针对MapReduce进行的优化是有限的,我们需要寻找更高效的计算引擎。Apache Spark作为新一代的分布式计算框架近年来快速发展壮大,性能日趋稳定,已经基本上可以完全取代MapReduce,因此Kylin从v2.0版本开始引入Spark作为Cube的计算引擎。

◆ 8.1 为什么要引入Apache Spark

由于MapReduce框架的设计具有局限性,一轮MapReduce任务只能处理一个Map任务和一个Reduce任务,中间结果需要保存到磁盘以供下一轮任务使用,如果有多轮任务的话,必然需要消耗大量的网络传输和磁盘I/O,而这些都是非常耗时的操作,导致MapReduce构建Cube的性能不佳。

在Kylin v2.0版本中,我们引入Apache Spark作为Cube的计算引擎。Apache Spark是一个开源的分布式计算框架,它提供了一个集群的分布式内存抽象(RDD),以及基于RDD的一系列灵活的应用程序编程接口。Spark是基于内存的迭代计算框架,不依赖Hadoop MapReduce的两阶段范式,由RDD组成有向无环图(DAG),RDD的转换操作会生成新的RDD,新的RDD的数据依赖父RDD保存在内存中的数据,这使得对于在重复访问相同数据的场景下,重复访问的次数越多、访问的数据量越大,引入Apache Spark作为Cube计算引擎的收益也就越大。由于这种在内存中迭代计算的设计非常符合Cube分层构建算法,加上受益于Kylin的可插拔架构,我们扩展Spark作为Kylin构建Cube的计算引擎。

◆ 8.2 Spark构建原理

使用分层构建算法构建一个包含4个维度的Cube,第一轮MR任务从源数据聚合得到4维的Cuboid,也就是Base Cuboid,第二轮MR任务由4维的Cuboid聚合得到3维的Cuboid,以此类推,在经过N+1轮MR任务后,所有的Cuboid都被计算出来。

MapReduce使用了“分层”算法进行构建,分层构建算法的思想就是把一个大的构建任务分成多个步骤来完成,并且每个步骤都是在上一步骤输出的基础上进行的,这样不但可以重用上一步骤的计算结果,而且如果当前步骤计算出错的话,整个计算任务不用从头开始,只需要基于上一步骤开始就可以了。因此可以看出分层构建的算法是可“依赖”的算法,由于Spark中的RDD也是可依赖的,新的RDD的数据依赖于父RDD保存在内存中的数据,因此我们在Spark中构建Cube时依然使用分层构建算法。

第N层N维的Cuboid可以看作一个RDD,那么一个有N个维度的Cube就会生成N+1个RDD,这些RDD是父子关系,N维的RDD由N-1维的RDD生成,并且父RDD是缓存在内存中的RDD.persist(StorageLevel),这使得Spark构建会比MR构建更加高效,因为进行MR构建的时候,父层数据是存储在磁盘上的,为了最大化地利用Spark的内存,父RDD在生成子RDD后需要从内存中释放RDD.unpersist(),而且每一层的RDD都会通过Spark提供的API持久化保存到HDFS上。这样经过N+1层的迭代,就完成了所有Cuboid的计算。

◆ 8.3.2 开启Spark动态资源分配

在Spark中,所谓资源单位一般指的是executor,和YARN中的容器一样,在Spark On YARN模式下,通常使用“num-executors”来指定应用程序使用的executor的数量,而executor-memory和executor-cores分别用于指定每个executor所使用的内存和虚拟CPU核数。

◆ 8.4 使用Spark SQL创建中间平表

Kylin默认的数据源是Hive,构建Cube的第一步是创建Hive中间平表,也就是把事实表和维度表连接成一张平表,这个步骤是通过调用数据源的接口来完成的。

◆ 8.5 小结

Kylin v2.0版本中引入了Spark作为Cube的构建引擎,并且在Kylin v2.5版本中实现了完全使用Spark,显著提高了Kylin的构建速度,也为Kylin完整地运行于非Hadoop环境(如Spark onKubernetes)提供了可能。

推荐开启Spark的动态资源分配,让Spark根据负载自动增加或减少构建资源,因为Spark的性能主要依赖集群的Memory和CPU资源,假设要构建一个数据量很大又有复杂数据模型的Cube,如果没有给这个构建任务足够的资源,那么Spark executors很可能会出现“OutOfMemorry”异常。对于那些有UHC维度,并且有很多维度组合,同时又包含非常占用内存的度量(如Count Distinct、Top_N)的Cube,建议使用MapReduce引擎进行构建,这会相对比较稳定。对于简单的Cube模型,如所有的度量都是“SUM/MIN/MAX/COUNT”且拥有中等规模的数据量,那么使用Spark构建引擎是一个很好的选择。

◆ 9.1.1 背景

OLAP的价值可体现在实现精细化运营、提升数据处理效率、改善数据可视化效果等多个方面。但小米公司内部的业务种类异常繁杂,各业务团队为了具备多维数据分析能力而各自建立了独立的OLAP分析系统。这些OLAP引擎大多采用指标数据先进入MySQL,再在前端展示的方法,而这样一来就会面临以下问题:1)基于MySQL的架构,在大数据上的查询效率低下;2)业务间OLAP引擎不统一,数据管道冗长,数据复用率极低,开发工作周期变长,维护成本增加;3)缺乏统一的维表和事实表,同主题下的数据统计口径不一致;4)新增业务需要投入较大的成本才能获得基础的OLAP能力。

◆ 第10章 扩展Apache Kylin

Apache Kylin有着卓越的可扩展架构。总体架构上的三大依赖——数据源、计算引擎和存储引擎都有清晰的接口,保证了Apache Kylin可以方便地接入最新的数据源或切换计算存储技术,从而跟随大数据生态圈一起演进。

◆ 11.1 身份验证

Kylin的Web模块使用Spring框架构建,在安全实现上选择了Spring Security。Spring Security是Spring项目组中用来提供安全认证服务的框架,它广泛支持各种身份验证模式,这些验证模型大多由第三方提供,Spring Security也提供了自己的一套验证功能。

在kylinSecurity.xml里,Kylin提供了三个配置profile:“testing”“ldap”和“saml”,依次对应三种用户验证方式:自定义验证、LDAP验证和单点登录验证。

◆ 11.1.1 自定义验证

自定义验证是基于配置文件的一种简单验证方式,由于它对外依赖少,开箱即用,所以是Kylin默认的用户验证方式;但由于其缺乏灵活性且安全性低,建议仅在测试阶段使用,故起名为“testing”。

◆ 11.1.2 LDAP验证

LDAP(Lightweight Directory Access Protocol,轻量级目录访问协议)用于提供被称为目录服务的信息服务。目录以树状的层次结构来存储数据,可以存储包括组织信息、个人信息、Web链接、JPEG图像等各种信息。支持LDAP协议的目录服务器产品有很多,大多企业也都使用LDAP服务器来存储和管理公司的组织和人员结构,集成LDAP服务器完成用户验证,不仅可以避免重复创建用户、管理群组等烦琐的管理步骤,还可以提供更高的便捷性和安全性。

◆ 11.1.3 单点登录

单点登录(Single Sign On,SSO)是一种高级的企业级认证服务。用户只需要登录一次,就可以访问所有相互信任的应用系统;它具有一个账户多处使用、避免了频繁登录、降低了信息的泄漏风险等优点。安全断言标记语言(Security Assertion Markup Language,SAML)是一个基于XML的标准语言,用于在不同的安全域(Security Domain)之间交换认证和授权数据。SAML是实现SSO的一种标准化技术,由国际标准化组织OASIS制定并发布。

启用SSO后,当用户初次在浏览器中访问Kylin的时候,Kylin会将用户转向SSO提供的登录页面,用户在SSO登录页面验证成功后,自动跳转回Kylin页面,这时Kylin会解析到SAML中的信息,获取验证后的用户名,再查询LDAP获取用户群组信息,赋予用户权限,完成验证登录。

◆ 11.2.1 新的访问权限控制

Apache Kylin中的项目级别设置了四种类型的访问权限角色。它们分别是ADMIN、MANAGEMENT、OPERATION和QUERY。并为每个角色都定义了用户可以在Apache Kylin中执行的功能列表。●QUERY:查询访问权限,旨在供仅需要访问权限的分析师用于查询项目中的表或Cube。●OPERATION:操作访问权限,旨在供需要权限以维护Cube的公司或组织的运营团队使用。OPERATION访问权限包括了QUERY。●MANAGEMENT:管理访问权限,是用于完全了解数据、模型和Cube的商业含义的模型管理和Cube设计。管理访问权限包括OPERATION和QUERY。●ADMIN:项目管理访问权限,旨在完全管理项目。ADMIN访问权限包括MANAGEMENT、OPERATION和QUERY。

◆ 11.2.3 管理数据访问权限

Apache Kylin提供了数据访问功能,方便用户对数据进行不同粒度的数据管控。目前Kylin支持表级访问权限,即控制了用户和群组在Kylin中能查询的表。当用户和群组被限制了对某表的访问权限时,用户/组便不能查询该表,不论是通过Cube还是查询下压。

◆ 15.2 实时流分析

Apache Kylin社区正在积极研发第三代流式构建技术。力求在Cube的基础上再添加实时节点,将最后几分钟的数据缓存在内存中。将实时节点联合Cube的内容能构成实时查询的Lambda架构,实现通过一个SQL接口同时查询历史和实时数据,真正做到秒级别延迟的实时大数据分析。

◆ 15.3 更快的存储和查询

当前的存储引擎HBase有着比较明显的短板(单索引,对Rowkey的设计要求高),架构复杂、运维难,因此Apache Kylin社区也在探索其他更快的储存技术,比如Druid或Parquet,甚至有人建议使用Cassandra或ElasticSearch,应用这些存储技术后的只读性能会成倍于HBase。因此,在HBase之外支持其他更快速的储存引擎也是一个重要的发展方向。Apache Kylin社区已经实现了Kylin onDruid和Kylin on Parquet的可选方案,期待在未来的某个版本正式发布。

你可能感兴趣的:(大数据,数据仓库,apache,kylin,大数据,分布式)