2.4.3 创建Cube
本节简单介绍了创建Cube时的各种配置选项,但是由于篇幅限制,这里没有对Cube的配置和优化进行进一步展开介绍。读者可以在后续的章节(如第3章“Cube优化”)中找到关于Cube的配置和优化的更详细的介绍。接下来开始Cube的创建。点击“New”→“New Cube”命令会开启一个包含若干步骤的向导。
第一步,选择要使用的数据模型,并为此Cube输入一个唯一的名称(必需的)和描述(可选)(如图2-9所示);这里还可以输入一个邮件通知列表,以在构建完成或出错时收到通知。如果不想接收在某些状态的通知,可以从“Notification Events”中将其去掉。
图2-9 设置Cube基本信息
第二步,添加Cube的维度。点击“Add Dimension”按钮添加维度,Apache Kylin会用一个树状结构呈现出所有列,用户只需勾选想要的列即可,同时需要为每个维度输入名字,可以设定是普通维度或是衍生(Derived)维度(如图2-10所示)。如果被设定为衍生维度的话,由于这些列值都可以从该维度表的主键值中衍生出来,所以实际上只有主键列会被Cube加入计算。而在Apache Kylin的具体实现中,往往采用事实表上的外键替代主键进行计算和存储。但是逻辑上可以认为衍生列来自维度表的主键。
图2-10 添加Cube的维度
第三步,创建度量。Apache Kylin支持的度量有SUM、MIN、MAX、COUNT、COUNT_DISTINCT、TOP_N、EXTENDED_COLUMN、PERCENTILE等。默认Apache Kylin会创建一个Count(1)度量。
可以通过点击“Bulk Add Measure”按钮批量添加度量。目前对于批量添加度量,Apache Kylin只支持SUM、MIN、MAX等简单函数。只需要选择度量类型,然后再选择需要计算的列,如图2-11所示。
图2-11 批量添加度量
如果需要添加复杂度量,可以点击“+Measure”按钮来添加新的度量。请选择需要的度量类型,然后再选择适当的参数(通常为列名)。图2-12所示为一个SUM(price)的示例。
图2-12 添加度量
重复以上操作,创建所需要的度量。Apache Kylin可以支持在一个Cube中有上百个的度量,添加完所有度量后,点击“Next”按钮,如图2-13所示。
图2-13 度量列表
第四步,进行关于Cube数据刷新的设置(如图2-14所示)。在这里可以设置自动合并的阈值、自动合并触发时保留的阈值、数据保留的最小时间,以及第一个Segment的起点时间(如果Cube有分割时间列),详细内容请参考第4章。
图2-14 进行刷新设置
第五步,高级设置。在此页面可以设置维度聚合组和Rowkey属性。
默认Apache Kylin会把所有维度放在同一个聚合组(Aggregation Group,也称维度组)中,如果维度数较多(如>15),建议用户根据查询的习惯和模式,点击“New Aggregation Group+”命令,将维度分布到多个聚合组中。通过使用多个聚合组,可以大大降低Cube中的Cuboid数量。
举例说明,一个Cube有(M+N)个维度,如果把这些维度都放置在一个组里,那么默认会有2(M+N)个Cuboid;如果把这些维度分为两个不相交的聚合组,第一个组有M个维度,第二个组有N个维度,那么Cuboid的总数量将被减至2M+2N,比之前的2(M+N)极大地减少了。
在单个聚合组中,可以对维度设置一些高级属性,如Mandatory Dimensions、Hierarchy Dimensions、Joint Dimensions等。这几种属性都是为优化Cube的计算而设计的,了解这些属性的含义对于更好地使用Cube至关重要。
强制维度(Mandatory Dimensions):指的是那些总是会出现在Where条件或Group By语句里的维度。通过指定某个维度为强制维度,Apache Kylin可以不预计算那些不包含此维度的Cuboid,从而减少计算量。
层级维度(Hierarchy Dimensions):是指一组有层级关系的维度,如“国家”“省”“市”,这里“国家”是高级别的维度,“省”“市”依次是低级别的维度。用户会按高级别维度进行查询,也会按低级别维度进行查询,但当查询低级别维度时,往往会带上高级别维度的条件,而不会孤立地审视低维度的数据。例如,用户会点击“国家”作为维度来查询汇总数据,也可能点击“国家”+“省”,或者“国家”+“省”+“市”来进行查询,但是不会跨越“国家”直接点击“省”或“市”来进行查询。通过指定层级维度,Apache Kylin可以略过不满足此模式的Cuboid。
联合维度(Joint Dimensions):是将多个维度视作一个维度,在进行组合计算的时候,它们要么一起出现,要么均不出现,通常适用于以下几种情形:
●总是一起查询的维度;
●彼此之间有一定映射关系,如USER_ID和EMAIL;
●基数很低的维度,如性别、布尔类型的属性。
如图2-15所示,先通过在“Includes”中选择要添加的维度到本聚合组中,然后根据模型特征和查询模式,设置高级维度属性。“Hierarchy Dimensions”和“Joint Dimensions”可以设置多组,但要注意,一个维度出现在某个属性中后,将不能再设置另一种属性。但是一个维度,可以出现在多个聚合组中。
图2-15 高级设置
在Apache Kylin中是以Key-Value的形式将Cube的构建结果存储到Apache HBase中的。我们知道,HBase是一种单索引、支持超宽表的数据存储引擎。HBase的Rowkey,即行键是用来检索其他列的唯一索引。Apache Kylin需要按照多个维度来对度量进行检索,因此在存储到HBase的时候,需要将多个维度值进行拼接组成Rowkey。图2-16中介绍了Apache Kylin将Cube存储在HBase中的原理。
图2-16 Cube存储到HBase
由于同一维度中的数值长短不一,如国家名,短的如“中国”,长的如“巴布亚新几内亚”,因此将多个不同列的值进行拼接的时候,要么添加分隔符,要么通过某种编码使各个列所占的宽度固定。Apache Kylin为了能够在HBase上高效地进行存储和检索,会使用第二种方式对维度值进行编码。维度编码的优势如下:
●压缩信息存储空间;
●提高扫描效率,减少解析开销。
编码(Encoding)代表了该维度的值使用何种方式进行编码,默认采用字典(Dictionary)编码技术。而合适的编码能够减少维度对空间的占用。例如,我们可以把所有的日期用三个字节进行编码,相比于使用字符串,或者使用长整数形式进行存储,我们的编码方式能够大大减少每行Cube数据的体积。而Cube中可能存在数以亿计的行,累加起来使用编码节约的空间将是非常庞大的。
目前Apache Kylin支持的编码方式有以下几种。
●Dictionary编码:字典编码是将所有此维度下的值构建成一张映射表,从而大大节约存储空间。另外,字典是保持顺序的,这样可以使得在HBase中进行比较查询的时候,依然使用编码后的值,而无须解码。Dictionary的优势是,产生的编码非常紧凑,尤其在维度的值基数小且长度大的情况下,Dictionary编码特别节约空间。由于产生的字典在使用时加载进构建引擎和查询引擎,所以在维度的基数大、长度也大的情况下,容易造成构建引擎或者查询引擎的内存溢出。在Apache Kylin中,字典编码允许的基数上限默认是500万(由其参数“kylin.dictionary.max.cardinality”配置)。
●Date编码:将日期类型的数据使用三个字节进行编码,支持从0000-01-01到9999-01-01中的每一个日期。
●Time编码:仅支持表示从1970-01-0100:00:00到2038-01-1903:14:07的时间,且Timestamp类型的维度经过编码和反编码之后,会失去毫秒信息,所以说Time编码仅仅支持到秒。但是Time编码的优势是每个维度仅使用四个字节,相比普通的长整数编码节约了一半空间。如果能够接受秒级的时间精度,可以选择Time来编码代表时间的维度。
●Integer编码:Integer编码适合于对int或bigint类型的值进行编码,它无须额外存储,同时可以支持很大的基数。使用时需要提供一个额外的参数“Length”来代表需要多少个字节。“Length”的长度为1~8。如果用来编码int32类型的整数,可以将“Length”设为“4”;如果用来编int64类型的整数,可以将“Length”设为“8”。在多数情况下,如果我们知道一个整数类型维度的可能值都很小,那么就能使用“Length”为“2”甚至是“1”的int编码来存储,这能够有效避免存储空间的浪费。
●Fixed_length编码:该编码需要提供一个额外的参数“Length”来代表需要多少个字节。对于基数大、长度也大的维度来说,使用Dict可能不能正常执行,于是可以采用一段固定长度的字节来存储代表维度值的字节数组,该数组为字符串形式的维度值的UTF-8字节。如果维度值的长度大于预设的Length,那么超出的部分将会被截断。此编码方式其实只是将原始值截断或补齐成相同长度的一组字节,没有额外的转换,所以空间效率较差,通常只是一种权宜手段。
在未来,Apache Kylin还有可能为特定场景、特定类型的维度量身定制特别的编码方式,如在很多行业,身份证号码可能是一个重要的维度。但是身份证号码由于其特殊性而不能使用整数类型的编码(身份证号码的最后一位可能是X),其高基数的特点也决定了其不能使用Dict编码,在目前的版本中只能使用Fixed_length编码,但显然Fixed_length不能充分利用身份证号码中大部分字节是数字的特性来进行深度编码,因此存在一定程度的存储空间的浪费。
同时,各个维度在Rowkey中的顺序,也会对查询的性能产生较明显的影响。在这里用户可以根据查询的模式和习惯,通过拖曳的方式调整各个维度在Rowkey上的顺序(如图2-17所示)。一般原则是,将过滤频率高的列放置在过滤频率低的列之前,将基数高的列放置在基数低的列之前。这样做的好处是,充分利用过滤条件来缩小在HBase中扫描的范围,从而提高查询的效率。
图2-17 Rowkey设置
在构建Cube时,可以通过维度组合白名单(Mandatory Cuboids)确保想要构建的Cuboid能被成功构建(如图2-18所示)。
图2-18 指定维度组合白名单
Apache Kylin支持对于复杂的COUNT DISTINCT度量进行字典构建,以保证查询性能。目前提供两种字典格式,即Global Dictionary和Segment Dictionary(如图2-19所示)。
图2-19 添加高级字典
其中,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,那么最好使用Segment Dictionary而不是Global Dictionary,这样可以避免单个字典过大的问题。
Apache Kylin目前提供的Cube构建引擎有两种:MapReduce和Spark(如图2-20所示)。如果你的Cube只有简单度量(如SUM、MIN、MAX),建议使用Spark。如果Cube中有复杂类型度量(如COUNT DISTINCT、TOP_N),建议使用MapReduce。
图2-20 构建引擎设置
为了提升构建性能,你可以在Advanced Snapshot Table中将维表设置为全局维表,同时提供不同的存储类型(如图2-21所示)。
图2-21 全局维表设置
在构建时Apache Kylin允许在Advanced Column Family中对度量进行分组(如图2-22所示)。如果有超过一个的COUNT DISTINCT或Top_N度量,你可以将它们放在更多列簇中,以优化与HBase的I/O。
图2-22 度量分组设置
第五步,为Cube配置参数。和其他Hadoop工具一样,Apache Kylin使用了很多配置参数,用户可以根据具体的环境、场景等配置不同的参数进行灵活调优。Apache Kylin全局的参数值可以在conf/kylin.properties文件中进行配置;如果Cube需要覆盖全局设置的话,需在此页面指定。点击“+Property”按钮,然后输入参数名和参数值,如图2-23所示,指定“kylin.hbase.region.cut”的值为“1”,这样,此Cube在存储的时候,Apache Kylin将会按每个HTable Region存储空间为1GB来创建HTable Region。如果用户希望任务从YARN中获取更多内存,可以设置kylin.engine.mr.config-override.mapreduce.map.memory.mb、kylin.engine.mr.config-override.mapreduce.map.java.opts等mapreduce相关参数。如果用户希望Cube的构建任务使用不同的YARN资源队列,可以设置kylin.engine.mr.config-override.mapreduce.job.queuename。这些配置均可以在Cube级别重写。
图2-23 覆盖默认参数
然后点击“Next”按钮到最后一个确认页面,如有修改,点“Prev”按钮返回进行修改,最后点“Save”按钮进行保存,一个Cube就创建完成了。创建好的Cube会显示在“Cubes”列表中,如要对Cube的定义进行修改,只需点“Edit”按钮就可以修改。也可以展开此Cube行以查看更多信息,如JSON格式的元数据、访问权限、通知列表等。