第3章
增量?构建
第2章介绍了如何构建cube并利用其完成在线多维分析的查询。每次cube的构建都会从hive中批量读取数据,而对于大多数业务场景来说,hive中的数据处于不断增长的状态。为了支持cube中的数据能够不断地得到更新,且无需重复地为已经处理过的历史数据构建cube,因此对于cube引入了增量构建的功能。
我们将cube划分为多个segment,每个segment用起始时间和结束时间来标志。segment代表一段时间内源数据的预计算结果。在大部分情况下(例外情况见第4章“流式构建”),一个segment的起始时间等于它之前那个segment的结束时间,同理,它的结束时间等于它后面那个segment的起始时间。同一个cube下不同的segment除了背后的源数据不同之外,其他如结构定义、构建过程、优化方法、存储方式等都完全相同。
本章将首先介绍如何设计并创建能够增量构建的cube,然后介绍实际测试或生产环境中触发增量构建的方法,最后将会介绍如何处理由于增量构建而导致的segment碎片,以保持kylin的查询性能。
<b></b>
<b>3.1 为什么要增量构建</b>
全量构建可以看作增量构建的一种特例:在全量构建中,cube中只存在唯一的一个segment,该segment没有分割时间的概念,因此也就没有起始时间和结束时间。全量构建和增量构建各有其适用的场景,用户可以根据自己的业务场景灵活地进行切换。全量构建和增量构建的详细对比如表3-1所示。
表3-1 全量构建和增量构建的对比
全量构建 增量构建
每次更新时都需要更新整个数据集 每次只对需要更新的时间范围进行更新,因此离线计算量相对较小
查询时不需要合并不同segment的结果 查询时需要合并不同segment的结果,因此查询性能会受到影响
不需要后续的segment合并 累计一定量的segment之后,需要进行合并
适合小数据量或全表更新的cube 适合大数据量的cube
对于全量构建来说,每当需要更新cube数据的时候,它不会区分历史数据和新加入的数据,也就是说,在构建的时候会导入并处理所有的原始数据。而增量构建只会导入新segment指定的时间区间内的原始数据,并只对这部分原始数据进行预计算。为了验证这个区别,可以到kylin的monitor页面观察构建的第二步——创建hive中间表(create intermediate flat hive table),单击纸张形的log按钮即可观察该步骤的参数:
insert overwrite table
kylin_intermediate_test_kylin_cube_without_slr_left_join_desc_20120601000000_20130101000000 select
test_kylin_fact.cal_dt
,test_kylin_fact.leaf_categ_id
,test_kylin_fact.lstg_site_id
,test_category_groupings.meta_categ_name
,test_category_groupings.categ_lvl2_name
,test_category_groupings.categ_lvl3_name
,test_kylin_fact.lstg_format_name
,test_kylin_fact.slr_segment_cd
,test_kylin_fact.price
,test_kylin_fact.item_count
,test_kylin_fact.seller_id
,test_sites.site_name
from default.test_kylin_fact as test_kylin_fact
left join edw.test_cal_dt as test_cal_dt
on test_kylin_fact.cal_dt = test_cal_dt.cal_dt
left join default.test_category_groupings as test_category_groupings
on test_kylin_fact.leaf_categ_id = test_category_groupings.leaf_categ_id and test_kylin_fact.lstg_site_id = test_category_groupings.site_id
left join edw.test_sites as test_sites
on test_kylin_fact.lstg_site_id = test_sites.site_id
left join edw.test_seller_type_dim as test_seller_type_dim
on test_kylin_fact.slr_segment_cd = test_seller_type_dim.seller_type_cd
where (test_kylin_fact.cal_dt >= '2012-06-01' and test_kylin_fact.cal_dt < '2013-01-01')
distribute by rand();
该构建任务对应于名为test_kylin_cube_without_slr_left_join_empty的cube构建,其seg-ment所包含的时间段为从2012-06-01(包含)到2013-01-01(不包含),可以看到在导入数据的hive命令中带入了包含这两个日期的过滤条件,以此保证后续构建的输入仅包含2012-06-01到2013-01-01这段时间内的数据。这样的过滤能够减少增量构建在后续的预计算中所需要处理的数据规模,有利于减少集群的计算量,加速segment构建的时间。
其次,增量构建的cube和全量构建的cube在查询时也有不同。对于增量构建的cube,由于不同时间的数据分布在不同的segment之中,因此为了获得完整的数据,查询引擎需要向存储引擎请求读取各个segment的数据。当然,查询引擎会根据查询中的条件自动跳过不感兴趣的segment。对于全量构建的cube,查询引擎只需要向存储引擎访问单个segment所对应的数据,从存储层返回的数据无需进行segment之间的聚合,但是这也并非意味着查询全量构建的cube不需要查询引擎做任何额外的聚合,为了加强性能,单个segment的数据也有可能被分片存储到引擎的多个分区上(参考第6章),从而导致查询引擎可能仍然需要对单个segment不同分区的数据做进一步的聚合。当然,整体来说,增量构建的cube上的查询会比全量构建的做更多的运行时聚合,而这些运行时聚合都发生在单点的查询引擎之上,因此通常来说增量构建的cube上的查询会比全量构建的cube上的查询要慢一些。
可以看到,日积月累,增量构建的cube中的segment越来越多,根据上一段的分析可以猜测到该cube的查询性能也会越来越慢,因为需要在单点的查询引擎中完成越来越多的运行时聚合。为了保持查询性能,cube的管理员需要定期地将某些segment合并在一起,或者让cube根据segment保留策略自动地淘汰那些不会再被查询到的陈旧segment。关于这部分的详细内容会在3.4.1节中展开详细讨论。
最后,我们可以得到这样的结论:对于小数据量的cube,或者经常需要全表更新的cube,使用全量构建需要更少的运维精力,以少量的重复计算降低生产环境中的维护复杂度。而对于大数据量的cube,例如,对于一个包含两年历史数据的cube,如果需要每天更新,那么每天为了新数据而去重复计算过去两年的数据就会变得非常浪费,在这种情况下需要考虑使用增量构建。