Cube划分为多个Segment(段,分段),每个Segment用起始时间和结束时间来标志。Segment代表一段时间内源数据的预计算结果。在大部分情况下一个Segment的起始时间等于它之前那个Segment的结束时间,同理,它的结束时间等于它后面那个Segment的起始时间。同一个Cube下不同的Segment除了背后的源数据不同之外,其他如结构定义、构建过程、优化方法、存储方式等都完全相同。
4.1 为什么要增量构建
全量构建可以看作增量构建的一种特例:在全量构建中,Cube中只存 在唯一的一个Segment,该Segment没有分割时间的概念,因此也就没有起始时间和结束时间。全量构建和增量构建各有其适用的场景,用户可以根据自己的业务场景灵活地进行切换。全量构建和增量构建的详细对比如下图:
对于全量构建来说,每当需要更新Cube数据的时候,它不会区分历史数据和新加入的数据,也就是说,在构建的时候会导入并处理所有的 原始数据。而增量构建只会导入新Segment指定的时间区间内的原始数 据,并只对这部分原始数据进行预计算
4.2 设计增量构建的前提:
并非所有的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。
4.3 触发增量构建
在Web GUI上触发Cube的增量构建与触发全量构建的方式基本相 同。在Web GUI的Model页面中,选中想要增量构建的Cube,单击 Action→Build。
不同于全量构建,增量构建的Cube会在此时弹出对话框让用户选 择“End Date”。 Kylin要求增量Segment的起始时间等于 Cube中最后一个Segment的结束时间,因此当我们为一个已经有Segment 的Cube触发增量构建的时候,“Start Date”的值已经被确定,且不能修改。
仅当Cube中不存在任何Segment,或者不存在任何未完成的构建任务 时,Kylin才接受该Cube上新的构建任务。
4.4 自动合并
在Cube Designer的“Refresh Settings”的页面中 有“Auto Merge Thresholds”和“Retention Threshold”两个设置项可以用来帮 助管理Segment碎片。
“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状态,自动合并的整套尝试又需要重新再来一遍。
举例来说,如果现在有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,尝试终止。
4.5 保留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之中,糟糕的是,这会不断地更新这个大Segment的结束时间,从而导致这个大 Segment永远不会得到释放。因此,推荐自动合并的最大一级的时间不要 超过1年。
4.6 数据持续更新
在实际应用场景中,我们常常会遇到这样的问题:由于ETL过程的延迟,业务每天都需要刷新过去N天的Cube数据。
举例来说,客户有一个报表每天都需要更新,但是每天的源数据更新不仅包含了当天的新数据, 还包括了过去7天内数据的补充。一种比较简单的方法是,每天在Cube中 增量构建一个长度为一天的Segment,这样过去7天的数据就会以7个 Segment的形式存在于Cube之中。Cube的管理员除了每天要创建一个新的 Segment代表当天的新数据(BUILD操作)以外,还需要对代表过去7天的7 个Segment进行刷新(REFRESH操作,Web GUI上的操作及Rest API参数与 BUILD类似,这里不再详细展开)。这样的方法固然可以奏效,但是每天 为每个Cube触发的构建数量太多,容易造成Kylin的任务队列堆积大量未能完成的任务。
上述简单方案的另外一个弊端是,每天一个Segment也会让Cube中迅 速地累积大量的Segment,需要Cube管理员手动地对历史已经超过7天的 Segment进行合并,期间还必须小心翼翼地,不能错将7天内的Segment一 起合并了。
举例来说,假设现在有100个Segment,每个Segment代表过去的 一天的数据,Segment按照起始时间排序。在合并时,我们只能挑选前面 93个Segment进行合并,如果不小心把第94个Segment也一起合并了,那么 当我们试图刷新过去7天(94100)的Segment的时候,会发现为了刷新第94天的数据,不得不将193的数据一并重新计算,这对于刷新来说是一种极大的浪费。糟糕的是,即使使用之前所介绍的自动合并的功能,类似的问题也仍然存在
解决思路:
不以日为单位创建新的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已经是最早的日期,所以不需要对更早的数据进行更新。
到01-08的时候,创建一个新的Segment B,它的时间是从01-08到01-15。此时我们不仅需要构建Segment B,还需要去刷新Segment A。因为01-01到01-07中的数据在01-08当天仍然可能处于更新状态。在接下来的01-09到01-14,每天刷新A、B两个Segment。等到了01-15这天的时候,首先创 建一个新的Segment C,它的时间是从01-15到01-22。在01-15当天, Segment A的数据应当已经被视作最终状态,因为Segment A中的最后一天 (01-07)已经不再过去N天的范围之内了。因此此时接下来只需要照顾 Segment B和Segment C即可。
由此可以看到,在任意一天内,我们只需要同时照顾两个Segment, 第一个Segment主要以刷新近期数据为主,第二个Segment则兼顾了加入新数据与刷新近期数据。这个过程中可能存在少量的多余计算,但是每 天多余计算的数据量不会超过N天的数据量。这对于Kylin整体的计算量 来说是可以接受的。根据业务场景的不同,N可能是7天,也有可能是30 天,我们可以适度地把最小的Segment设置成比N稍微大一点的数字,例 如N为7的时候,我们可以设置为10天,这样即使ETL有时候没有能够遵 守N=7的约定,也仍然能够刷新足够的数据。