Kylin(三)cube优化

Kylin构建优化

Kylin 新手必看:Cube 越用越好,存储越用越少

  • 文档:
    • https://cwiki.apache.org/confluence/display/KYLIN/Cuboid+Pruning+Optimization+In+Kylin_CN
    • https://cwiki.apache.org/confluence/display/KYLIN/System+Cube+Introduction_CN
  • 视频地址:
    • https://www.bilibili.com/video/BV1WV411b7Uh?from=search&seid=12051378144106067557

Kylin-4.0全网第一份 Kylin 4.0 性能调优指南!

  • 文档:
    • https://mp.weixin.qq.com/s?__biz=MzAwODE3ODU5MA==&mid=2653081761&idx=1&sn=4d5e3366de6e3fb8f82695b9e3734de1&chksm=80a4ae50b7d327462416c927c3de6dc30e575e30a9402e4a61dc14dd56010e9cee7992d8f226&scene=21#wechat_redirect
  • 视频地址:
    • https://search.bilibili.com/all?keyword=kylin%E4%BC%98%E5%8C%96&order=totalrank&duration=0&tids_1=36

关注点:

  1. Cube生成的cuboid数量减少
  2. 构建cube速度提升
  3. 减少存储cube的所占用空间

增量Cube

增量Cube,Cube划分为多个Segment,每个Segment用起始时间和结束时间来标志。Segment代表一段时间内源数据的预计算结果。

在大部分情况下一个Segment的起始时间等于它之前那个Segment的结束时间,同理,它的结束时间等于它后面那个Segment的起始时间。

同一个Cube下不同的Segment除了背后的源数据不同之外,其他如结构定义、构建过程、优化方法、存储方式等都完全相同。

为什么要增量构建存在唯一的一个Segment,该Segment没有分割时间的概念,因此也就没有起始时间和结束时间。

全量构建和增量构建各有其适用的场景,用户可以根据自己的业务场景灵活地进行切换。全量构建和增量构建的详细对比如下图:
在这里插入图片描述

对于全量构建来说,每当需要更新Cube数据的时候,它不会区分历史数据和新加入的数据,也就是说,在构建的时候会导入并处理所有的原始数据

而增量构建只会导入新Segment指定的时间区间内的原始数据,并只对这部分原始数据进行预计算。

设计增量构建的前提

并非所有的Cube都适用于增量构建,Cube的定义必须包含一个时间维度,用来分割不同的Segment

我们将这样的维度称为分割时间列 (Partition Date Column)。分割时间列既可以是Hive中的Date类型、也可以是Timestamp类型或 String类型。

无论是哪种类型,Kylin都要求用户显式地指定分割时间列的数据格式,例如精确到年月日的Date类型(或者String类型)的数据格式可能是yyyyMMdd或yyyy-MM-dd,如果是精确到时分秒的Timestamp类型(或者String类型),那么数据格式可能是YYYY-MM-DD HH:MM:SS。
在这里插入图片描述
在Web UI上触发Cube的增量构建与触发全量构建的方式基本相同。在Web UI的Model页面中,选中想要增量构建的Cube,单击 Action→Build

不同于全量构建,增量构建的Cube会在此时弹出对话框让用户选择End Date。 Kylin要求增量Segment的起始时间等于 Cube中最后一个Segment的结束时间,因此当我们为一个已经有Segment 的Cube触发增量构建的时候,Start Date的值已经被确定,且不能修改。

仅当Cube中不存在任何Segment,或者不存在任何未完成的构建任务 时,Kylin才接受该Cube上新的构建任务。而如果不是web GUI触发,你通过restAPI触发的话,那么要确保你给定的时间,不重合。
在这里插入图片描述

自动合并

在Cube Designer的Refresh Settings(这一步骤是为增量构建 cube 而设计的。)的页面中有Auto Merge Thresholds和Retention Threshold两个设置项可以用来帮助管理Segment碎片。

Auto Merge Thresholds: 自动合并小的 segments 到中等甚至更大的 segment。如果不想自动合并,删除默认2个选项。

Volatile Range: 默认为0,会自动合并所有可能的 cube segments,或者用 ‘Auto Merge’ 将不会合并最新的 [Volatile Range] 天的 cubeSegments

Retention Threshold: 只会保存 cube 过去几天的 segment,旧的segment 将会自动从头部删除;0表示不启用这个功能。

Partition Start Date: cube 的开始日期.
在这里插入图片描述

Auto Merge Thresholds 允许用户设置几个层级的时间阈值,层级越靠后,时间阈值就越大。举例来说,用户可以为一个Cube指定(7天、28天)这样的层级。每当Cube中有新的READY状态的Segment,也就是刚构建完成一个Segment的时候。就会触发一次

系统试图自动合并的尝试。系统首先会尝试最大一级的时间阈值,结合上面的(7天、28天)层级的例子。。。

首先查看是否能将连续的若干个Segment合并成为一个超过或者等于28天的大Segment,在挑选连续Segment的过程中,如果遇到已经有个别Segment的时间长度本身已经超过等于了28天,那么系统会跳过该Segment,从它之后的所有Segment中选择 多个连续的并且累加起来超过或者等于28天的Segment。

如果满足条件的多个连续的Segment还不能够累积超过28天,那么系统会使用下一个层级的时间阈值重复寻找的过程。也就是 不去合并28Days 。 而是合并7Days。

每当找到了能够满足条件的连续Segment,系统就会触发一次自动合并Segment的构建任务,在构建任务完成之后,新的Segment被设置为READY状态,自动合并的整套尝试又需要重新再来一遍。

举例来说,如果现在有A~H8个连续的Segment,它们的时间长度分别 为28天(A)、7天(B)、1天(C)、1天(D)、1天(E)、1天(F)、1天(G)、1天 (H)。此时第9个Segment I加入,它的时间长度为1天,那么现在Cube中总 共存在9个Segment。

系统首先尝试能否将连续的Segment合并到28天这个阈值上,由于Segment A已经超过28天,它会被排除。接下来的B到H加起 来也不足28天,因此第一级的时间阈值无法满足,退一步系统尝试第二级的时间阈值,也就是7天。

系统重新扫描所有Segment,发现A和B已经超过7天,因此跳过它们,接下来发现将Segment C到I 合并起来可以达到7 天的阈值,因此系统提交一个合并Segment的构建请求,将Segment C到I 合并为一个新的segment X

X的构建完成之后,Cube中只剩下三个 Segment,分别是原来的A(28天),B(7天)和新的X(7天)。由于X的加入,触发了系统重新开始整个合并尝试,但是发现已经没有满足自动合并的条件,既没有连续的、满足条件的、累积超过28天的Segment,也没有连续的、满足条件的、累积超过7天的Segment,尝试终止。

保留Segment

从碎片管理的角度来说,自动合并是将多个Segment合并为一个 Segment,以达到清理碎片的目的。保留Segment则是从另外一个角度帮助实现碎片管理,那就是及时清理不再使用的Segment

在很多业务场景中,只会对过去一段时间内的数据进行查询,例如对于某个只显示过去1 年数据的报表,支撑它的Cube事实上只需要保留过去一年内的Segment即可。由于数据在Hive中往往已经存在备份,因此无需再在Kylin中备份超过一年的历史数据。

在这种情况下,我们可以将Retention Threshold设置为365。每当有新的Segment状态变为READY的时候,系统会检查每一个Segment:如果它的结束时间距离最新的一个Segment的结束时间已经大于Retention Threshold,那么这个Segment将被视为无需保留。系统会自动地从Cube中 删除这个Segment。

如果启用了Auto Merge Thresholds,那么在使用Retention Threshold的时候需要注意,不能将Auto Merge Thresholds 的最大层级设置得太高。假设我们将 Auto Merge Thresholds的最大一级设置为1000天。

而将Retention Threshold设置为365天,那么受到自动合并的影响, 新加入的Segment会不断地被自动合并到一个越来越大的Segment之中,但是这样会导致kylin要去不断地更新这个大Segment的结束时间,从而导致这个大 Segment永远不会得到释放。因此,推荐自动合并的最大一级的时间不要超过1年。

数据持续更新

在实际应用场景中,我们常常会遇到这样的问题:客户有一个报表每天都需要更新,但是每天的源数据更新不仅包含了当天的新数据, 还包括了过去7天内数据的补充,或者是对某些内容的修改。

一种比较简单的方法是,每天在Cube中增量构建一个长度为一天的Segment,这样过去7天的数据就会以7个Segment的形式存在于Cube中

Cube的管理员除了每天要创建一个新的 Segment代表当天的新数据(BUILD操作)以外,还需要对代表过去7天的7个Segment进行刷新(REFRESH操作,Web UI上的操作及Rest API参数与BUILD类似,这里不再详细展开)。这样的方法固然可以奏效,但是每天为每个Cube触发的构建数量太多,容易造成Kylin的任务队列堆积大量未能完成的任务。

上述简单方案的另外一个弊端是,每天一个Segment也会让Cube中迅速地累积大量的Segment,需要Cube管理员手动地对历史已经超过7天的 Segment进行合并,剩下的都是1天的多个Segment。该过程期间还必须小心地操作,不能错将7天内的Segment一 起合并了。

否则更新7天历史数据的时候。因不小心将7天的历史数据合并到最大的那个Segment中。那就需要对最大的Segment执行刷新操作。(浪费资源)

解决思路:
不以日为单位创建新的Segment, 而是以N天为单位创建新的Segment。
举例来说,假设用户每天更新Cube 的时候,前面7天的数据都需要更新一下,也就是说,如果今天是01-08, 那么用户不仅要添加01-08的新数据,还要同时更新01-01到01-07的数据。

在这种情况下,可设置N=7作为最小Segment的长度。在第一天01-01, 创建一个新的Segment A,它的时间是从01-01到01-08,我们知道Segment 是起始时间闭,结束时间开,因此Segment A的真实长度为7天,也就是01-01到01-07。即使在01-01当天,还没有后面几天的数据,Segment A也能正 常地构建,只不过构建出来的Segment其实只有01-01一天的数据而已。

从 01-02到01-07的每一天,我们都要刷新Segment A,以保证1日到7日的数据保持更新。由于01-01已经是最早的日期,所以不需要对更早的数据进行更新。由此可以看到,在任意一天内,我们只需要同时照顾两个Segment, 第一个Segment主要以刷新近期数据为主,第二个Segment则兼顾了加入新数据与刷新近期数据。

这个过程中可能存在少量的多余计算,但是每天多余计算的数据量不会超过N天的数据量。这对于Kylin整体的计算量来说是可以接受的。根据业务场景的不同,N可能是7天,也有可能是30天,我们可以适度地把最小的Segment设置成比N稍微大一点的数字。

Cube剪枝优化

聚合组Aggravation Group

Kylin在定义Cube时候,可以将维度拆分成多个聚合组(Aggregation Groups)这也就是我们在web页面创建cube时的第5步可以做,只在组内计算Cube,聚合组内查询效率高,跨组查询效率较差,所以需要根据业务场景,将常用的维度组合定义到一个聚合组中,提高查询性能,这也是Kylin中查询性能优化的一个重要方面。

举例:

业务场景有4个维度,分别为ABCD,如果聚合组含有的维度为ABCD的话,它的Cuboid为2^4=16个。 但是此时如果AB是一个聚合组,CD是一个聚合组,那么Cuboid的个数就是22+22=8个,相当于缩减了一半。即原来2^(K+M+N) 个Cuboid可以减少到2K+2M+2^N个。

查看cube生成的cuboid个数命令:

kylin.sh org.apache.kylin.engine.mr.common.CubeStatsReader CubeName

强制维度(Mandatory Dimensions)

如果一个维度被定义为强制维度,那么这个分组产生的所有Cuboid中每一个Cuboid都会包含该维度。如果根据这个分组的业务逻辑,相关的查询一定会在过滤条件或分组条件中,则可以在该分组中把该维度设置为强制维度,左侧图中为ABC三个维度,右侧将A设置为强制维度。(减少了Cuboid的数量)
在这里插入图片描述

层次维度(Hierarchy Dimensions)

具有上下级层次关系的维度,例如时间维度 年—>月—>日,和地区维度 国家(country)—>省份(province)—> 城市(city)。将有这样关系的维度设置为层次关系,并且将它们设置为层次维度的话,cuboid数量将下降

举例:
如果有三个维度A,B,C 设置为层次维度,那么Cuboid数量将由2^3=8个维度 消减为3+1 = 4 各维度 组合出来的维度为:(ABC、AB、A、空)
在这里插入图片描述

对应的group by就变成了如下三种情况

group by country, province, city(等同于 group by country, city 或者group by city)

group by country, province(等同于group by province)

group by country。

联合维度(Joint Dimensions)

每个联合中包含两个或更多个维度,如果某些列形成一个联合,那么在该分组产生的任何Cuboid中,这些联合维度 要么一 起出现,要么都不出现。如果根据这个分组的业务逻辑,多个维度在查询中总是同时出现,则可以在该分组中把这些维度设置为联合维度。

例子:如有 A B C D 四个维度, B C维度作为联合维度,则最后只要生成7个维度,如下图所示:其中红色维度为无需生成的维度,黑色维度为需要生成的维度。
在这里插入图片描述

衍生维度

衍生维度用于在有效维度内将维度表上的非主键维度排除掉,并使用维度表的主键(其实是事实表上相应的外键)来替代它们。

Kylin会在底层记录维度表主键与维度表其他维度之间的映射关系,以便在查询时能够动态地将维度表的主键翻译成这些非主键维度,并实时聚合

例如,假设有一个日期查找表,其中的cal_dt是主键列,还有许多派生列,例如week_begin_dt,month_begin_dt。即使分析人员需要 week_begin_dt作为维度,我们也可以对其进行修剪,因为它始终可以从cal_dt维度进行计算,这是“派生”优化。

说白了 虽然月份,周几,季度都可以作为维度。但是这些维度 都是由 cal_dt 派生 衍生出来的。Kylin真正构建Cube的时候 只需要一个 cal_dt 即可。如果我们后续需要对month,季度等做group by 也可以很快查询出来。因为Kylin底层会将cal_dt 和 季度,月份等作一个映射。能很快找到

Rowkeys

排序优化

Important: Dimension’s position on HBase rowkey is critical for performance. You can drag and drop to adjust the sequence. In short, put filtering dimension before non-filtering dimension, and put high cardinality dimension before low cardinality dimension.

Important: Dimension在HBase rowkey上的位置对性能至关重要。您可以通过拖放来调整序列。简而言之,将过滤维度置于非过滤维度之前,将高基数维度置于低基数维度之前。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EbmralSe-1618065108422)(Kylin - 麒麟.assets/image-20210209204500383.png)]

编码优化

编码(Encoding)代表了该维度的值应使用何种方式进行编码,合适的编码能够减少维度对空间的占用

例如,我们可以把所有的日期都用三个字节进行编码,相比于字符串存储,或者是使用长整数形式存储的方法,我们的编码方式能够大大减少每行Cube数据的体积。而Cube中可能存在数以亿计的行数,使用编码节约的空间累加起来将是一个非常巨大的数字。

目前Kylin支持的编码方式有以下几种:

  1. Date编码:将日期类型的数据使用三个字节进行编码,其支持从 000-01-01到9999-01-01中的每一个日期。
  2. Time编码:仅支持表示从1970-01-01 00:00:00到2038-01-19 03:14:07的时间,且Time-stamp类型的维度经过编码和反编码之后,会失去毫秒信息,所以说Time编码仅仅支持到秒。但是Time编码的优势是每个维度仅仅使用4个字节,这相比普通的长整数编码节约了一半。如果能够接受秒级的时间精度,请选择Time编码来代表时间的维度。
  3. Integer编码:Integer编码需要提供一个额外的参数Length来代表需要多少个字节。Length的长度为1~8。如果用来编码int32类型的整数,可以将Length设为4;如果用来编码int64类型的整数,可以将Length设为8。在更多情况下,如果知道一个整数类型维度的可能值都很小,那么就能使用 Length为2甚至是1的int编码来存储,这将能够有效避免存储空间的浪费。
  4. Dict编码:对于使用该种编码的维度,每个Segment在构建的时候都会为这个维度所有可能的值创建一个字典,然后使用字典中每个值的编 号来编码。Dict的优势是产生的编码非常紧凑,尤其在维度值的基数较小且长度较大的情况下,特别节约空间。由于产生的字典是在查询时加载入构建引擎和查询引擎的,所以在维度的基数大、长度也大的情况下,容易造成构建引擎或查询引擎的内存溢出。
  5. Fixed_length编码:编码需要提供一个额外的参数Length来代表需 要多少个字节。该编码可以看作Dict编码的一种补充。对于基数大、长度也大的维度来说,使用Dict可能不能正常工作,于是可以采用一段固定长度的字节来存储代表维度值的字节数组,该数组为字符串形式的维度值的UTF-8字节。如果维度值的长度大于预设的Length,那么超出的部分将会被截断。
  6. Fixed_Length_Hex编码:适用于字段值为十六进制字符,比如1A2BFF或者FF00FF,每两个字符需要一个字节。只适用于varchar或nvarchar类型。

Cube Engine

选择构建 Cube 的引擎
在这里插入图片描述

相关链接

https://mp.weixin.qq.com/s/Kb35rAq2TvdU1HsuV2m9eg

Kylin和Phoenix区别

https://www.cnblogs.com/bonelee/p/12442904.html

Kylin使用问题汇总

事务表

  1. 创建普通的Hive表,不能够支持 Update操作,必须在create table 的时候指定为 事务表。

    beeline -u 'jdbc:hive2://localhost:10000?hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;hive.support.concurrency=true' -n root
    
  2. 在使用Kylin操作 Hive表中使用 CASE 的时候 也不能够正常执行,需要在Kylin中加入以下参数

    kylin.query.enable-dynamic-column=true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值