内容简介:版权声明:本套技术专栏是作者(秦凯新)平时工作的总结和升华,通过从真实商业环境抽取案例进行总结和分享,并给出商业应用的调优建议和集群环境容量规划等内容,请持续关注本套博客。版权声明:禁止转载,欢迎学习。Kylin官方案例是一个非常经典的案例,包含的技术细节通过深挖能呈现出更具价值的信息,如:数据仓库理论,星型模型,雪花模型,角色扮演维度,维度剪枝等。相信您会通过我的深入剖析,有一种恍然大悟的感觉。官方案例是一个订单案例,其中包含了订单事实表,订单日期表,商品分类维度,账号维度(购买者和销售者)以及区域维度
版权声明:本套技术专栏是作者(秦凯新)平时工作的总结和升华,通过从真实商业环境抽取案例进行总结和分享,并给出商业应用的调优建议和集群环境容量规划等内容,请持续关注本套博客。版权声明:禁止转载,欢迎学习。
Kylin官方案例是一个非常经典的案例,包含的技术细节通过深挖能呈现出更具价值的信息,如:数据仓库理论,星型模型,雪花模型,角色扮演维度,维度剪枝等。相信您会通过我的深入剖析,有一种恍然大悟的感觉。官方案例是一个订单案例,其中包含了订单事实表,订单日期表,商品分类维度,账号维度(购买者和销售者)以及区域维度(购买者和销售者)。
一 Kylin官方案例表关系及字段详解
1.1 Kylin官方案例表简要说明
- KYLIN_SALES是事实表,保存了销售订单的明细信息。各列分别保存着卖家、商品分类、订单金额、商品数量等信息,每一行对应着一笔交易订单。
- KYLIN_CATEGORY_GROUPINGS是维表,保存了商品分类的详细介绍,例如商品分类名称等。
- KYLIN_CAL_DT也是维表,保存了时间的扩展信息。如单个日期所、月始、周始、年份、月份等。
- KYLIN_ACCOUNT也是维表,作为角色扮演维度,包含购买者和开发者账号。
- KYLIN_COUNTRY包含区域维度,作为角色扮演维度,包含购买者和开发者所在区域信息。
1.2 KYLIN_SALES 事实表字段详解:
1.3 KYLIN_CATEGORY_GROUPINGS 字段说明 :
1.3.1 KYLIN_CATEGORY_GROUPINGS主要字段:
- LEAF_CATEG_ID:主键
- SITE_ID:外键
1.3.2 KYLIN_CATEGORY_GROUPINGS 对外连接关系:
1.3.3 KYLIN_CATEGORY_GROUPINGS 主要字段解释:
1.4 KYLIN_COUNTRY 主要字段详解:
1.5 KYLIN_ACCOUNT 主要字段详解:
1.6 雪花模型关系总览:
二 建立模型(Model)
2.1 Kylin 官方模型 model关系图如下:
2.2 第一步:雪花和星型模型,建立inner join关系,重要的是确定字段关联,对于角色维度(KYLIN_ACCOUNT和KYLIN_COUNTRY),需要别名处理。
对 “Add Lookup Table” 页面的几点说明:
- 数据关系不仅仅是事实表与维度表之间(星型模型),维度表和维度表之间(雪花模型)也可以建立联系;
- 表与表之间的连接添加有三种:“Left Join”、“Inner Join”、“Right Join”;
- Skip snapshot for this lookup table 选项指的是是否跳过生成 snapshotTable,由于某些 Lookup 表特别大(大于 300M),如果某一个维度的基数比较大 ,可能会导致内存出现 OOM,所以在创建 snapshotTable 的时候会限制原始表的大小不能超过配置的一个上限值(kylin.snapshot.max-mb,默认值300);
- 跳过构建 snapshot 的 lookup 表将不能搜索,同时不支持设置为衍生维度(Derived);
- 大部分情况下都是使用 “Left Join”,其他两种 Join 方式不是很常用。
2.2.1 每一个 Snapshot 是和一个 Hive 维度表对应的,生成的过程是:
-
从原始的hive维度表中顺序得读取每一行每一列的值;
-
使用 TrieDictionary 方式对这些所有的值进行编码(一个值对应一个 Id);
-
再次读取原始表中每一行的值,将每一列的值使用编码之后的 Id 进行替换,得到了一个只有 Id 的新表;
-
同时保存这个新表和 Dictionary 对象(Id 和值的映射关系)就能够保存整个维度表;
-
Kylin 将这个数据存储到元数据库中。
该处Snapshot Table 总结的很好,所以标明引用地址:https://juejin.im/post/5bcf370d6fb9a05cff3255dd 复制代码
2.2.2 雪花模型关系图
2.3 第二步:资格维度选择:
在 Dimensions 页面选择可能参与计算的维度,这里被选择的只是在 Cube 构建的时候拥有被选择资格的维度,并不是最后参与 Cube 构建的维度,推荐将维度表中的字段都选择上。 如下展示了Dimensions的选择:
- 对于KYLIN_SALES,其中SLR_SEGMENT_CD,PRICE,ITEM_COUNT没有选择,所以没有资格参与cubeId构建
- 对于KYLIN_CAL_DT,其中仅选择了部分参与的维度,其他没有资格参与cubeId构建
- 对于KYLIN_CATEGORY_GROUPING,其中仅选择了部分参与的维度,其他没有资格参与cubeId构建
- 对于BUYER_ACCOUNT 与 SELLER_ACCOUNT,全部有资格参与cubeId构建
-
对于BUYER_COUNTRY,只有COUNTRY , NAME 有资格参与cubeId构建
-
对于SELLER_COUNTRY,只有COUNTRY , NAME 有资格参与cubeId构建
同理如上
2.3.1 资格维度选择结果总览:
2.4 第三步: Measures 度量指标选择:
在 Measures 页面选择可能用于计算的度量。一般而言,销售额、流量、温湿度等会作为度量。
2.5 第四步:Settings设置
在 Settings 页面可以设置分区以及过滤条件,其中分区是为了系统可以进行增量构建而设计的,目前 Kylin 支持基于日期的分区,在 “Partition Date Column” 后面选择事实表或者维度表中的日期字段,然后选择日期格式即可;过滤条件设置后,Kylin 在构建的时候会选择符合过滤条件的数据进行构建。 需要注意的几点:
- 时间分区列可以支持日期或更细粒度的时间分区;
- 时间分区列支持的数据类型有 time/date/datetime/integer等;
- 过滤条件不需要写 WHERE;
- 过滤条件不能包含日期维度。
三 构建cube模型
3.1 维度选择知识总结
在选择维度时,每一个维度列可以作为普通维度(Normal),也可以作为衍生维度(Derived)。相对于普通维度来说,衍生维度并不参与维度的 Cuboid,衍生维度对应的外键(FK)参与维度 Cuboid,从而降低 Cuboid 数。在查询时,对衍生维度的查询会首先转换为对外键所在维度的查询,因此会牺牲少量性能(大部分情况下可以接受)。
3.1.1 维度剪枝优化
如何进行维度优化,首先请确认你设置的cube维度都是你查询时会使用到的。
目前Kylin可以使用的维度优化手段有以下几种:
- 聚集组
- 衍生纬度
- 强制维度
- 层次维度
- 联合维度
- Extended Column
在一个多维数据集合中,维度的个数决定着维度之间可能的组合数,而每一个维度中成员集合的大小决定着每一个可能的组合的个数,例如有三个普通的维度A、B、C,他们的不同成员数分别为10/100/1000,那么一个维度的组合有2的3次方个,分别是{空、A、B、C、AB、BC、AC、ABC},每一个成员我们称为cuboid(维度的组合),而这些集合的成员组合个数分别为1、10、100、1000、10*100、100 1000、10 1000和10 *100 *1000。我们称每一个dimension中不同成员个数为cardinatily,我们要尽量避免存储cardinatily比较高的维度的组合。
在上面的例子中我们可以不缓存BC和C这两个cuboid,可以通过计算的方式通过ABC中成员的值计算出BC或者C中某个成员组合的值,这相当于是时间和空间的一个权衡吧。在kylin中存在的四种维度是为了减少cuboid的个数,而不是每一个维度是否缓存的,当前kylin是对所有的cuboid中的所有组合都进行计算和存储的,对于普通的dimension,从上面的例子中可以看出N个维度的cuboid个数为2的N次方,而kylin中设置了一些维度可以减少cuboid个数,当然,这需要使用者对自己需要的维度十分了解,知道自己可能根据什么进行group by。
3.1.2 Mandatory维度
这种维度意味着每次查询的group by中都会携带的,将某一个dimension设置为mandatory可以将cuboid的个数减少一半,如下图:
这是因为我们确定每一次group by都会携带A,那么就可以省去所有不包含A这个维度的cuboid了。
3.1.3 hierarchy维度
这种维度是最常见的,尤其是在mondrian中,我们对于多维数据的操作经常会有上卷下钻之类的操作,这也就需要要求维度之间有层级关系,例如国家、省、城市,年、季度、月等。有层级关系的维度也可以大大减少cuboid的个数。如下图:
这里仅仅局限于A/B/C是一个层级,例如A是年份,B是季度、C是月份,那么查询的时候可能的组合只有年、xx年的季度、xx年xx季度的xx月,这就意味着我们不能再单独的对季度和月份进行聚合了,例如我们查询的时候不能使用group by month,而必须使用group by year,quart,month。如果需要单独的对month进行聚合,那么还需要再使用month列定义一个单独的普通维度。
3.1.4 derived维度
这类维度的意思是可推导的维度,需要该维度对应的一个或者多个列可以和维度表的主键是一对一的,这种维度可以大大减少cuboid个数,如下图:
例如timeid是时间这个维度表的主键,也就是事实表的外键,时间只精确到天,那么year、month、day三列可以唯一对应着一个time_id,而time_id是事实表的外键,那么我们可以指定year、month、day为一个derived维度,实际存储的时候可以只根据timeid的取值决定维度的组合,但这就要求我们在查询的时候使用的group by必须指定derived维度集合中的所有列。 3.联合维度(Joint) 每一个联合维度包括两个或者更多的维度,联合维度内的维度,要么不出现,要么必须一起出现。不同的联合之间不应当有共同的维度
3.1.5 联合维度
联合维度:将几个维度视为一个维度。 适用场景:
-
1 可以将确定在查询时一定会同时使用的几个维度设为一个联合维度。
-
2 可以将基数很小的几个维度设为一个联合维度。
-
3 可以将查询时很少使用的几个维度设为一个联合维度。
优化效果:将N个维度设置为联合维度,则这N个维度组合成的cuboid个数会从2的N次方减少到1。
应用实例:
假设创建一个交易数据的Cube,它具有很多普通的维度,像是交易日期 cal_dt,交易的城市 city,顾客性别 sex_id 和支付类型 pay_type 等。分析师常用的分析方法为通过按照交易时间、交易地点和顾客性别来聚合,获取不同城市男女顾客间不同的消费偏好,例如同时聚合交易日期 cal_dt、交易的城市 city 和顾客性别 sex_id来分组。 聚合组:[cal_dt, city, sex_id,pay_type] 联合维度: [cal_dt, city, sex_id]
Case 1:
SELECT cal_dt, city, sex_id, count(*) FROM table GROUP BY cal_dt, city, sex_id 则它将从Cuboid [cal_dt, city, sex_id]中获取数据 复制代码
Case2:
如果有一条不常用的查询:
SELECT cal_dt, city, count(*) FROM table GROUP BY cal_dt, city 则没有现成的完全匹配的 Cuboid,Apache Kylin 会通过在线计算的方式,从现有的 Cuboid 中计算出最终结果。 复制代码
3.1.6 粒度优化
粒度优化对应的是提高Cube的并发度,其设置是在自定义属性中的 一共有三个属性可以提高并发度。 1.kylin.hbase.region.cut(共使用几个分区) 2.kylin.hbase.region.count.min(最少使用几个分区) 3.kylin.hbase.region.count.max(最多使用几个分区)
根据相对应的情况调高最少使用分区,降低最大使用分区,能够有效增加系统的并行度。
3.1.7 RowKey优化
Rowkeys: 是由维度编码值组成。”Dictionary” (字典)是默认的编码方式; 字典只能处理中低基数(少于一千万)的维度;如果维度基数很高(如大于1千万), 选择 “false” 然后为维度输入合适的长度,通常是那列的最大长度值; 如果超过最大值,会被截断。请注意,如果没有字典编码,cube 的大小可能会非常大。 你可以拖拽维度列去调整其在 rowkey 中位置; 位于rowkey前面的列,将可以用来大幅缩小查询的范围。通常建议将 mandantory 维度放在开头, 然后是在过滤 ( where 条件)中起到很大作用的维度;如果多个列都会被用于过滤,将高基数的维度(如 user_id)放在低基数的维度(如 age)的前面。
Kylin 以 Key-Value 的方式将 Cube 存储到 HBase 中,HBase 的 key,也就是 Rowkey,是由各维度的值拼接而成的;为了更高效地存储这些值,Kylin 会对它们进行编码和压缩;每个维度均可以选择合适的编码(Encoding)方式,默认采用的是字典(Dictionary)编码技术;字段支持的基本编码类型如下:
-
dict:适用于大部分字段,默认推荐使用,但在超高基情况下,可能引起内存不足的问题;
-
boolean:适用于字段值为true, false, TRUE, FALSE, True, False, t, f, T, F, yes, no, YES, NO, Yes, No, y, n, Y, N, 1, 0;
-
integer:适用于字段值为整数字符,支持的整数区间为[ -2^(8N-1), 2^(8N-1)];
-
date:适用于字段值为日期字符,支持的格式包括yyyyMMdd、yyyy-MM-dd、yyyy-MM-dd HH:mm:ss、yyyy-MM-dd HH:mm:ss.SSS,其中如果包含时间戳部分会被截断;
-
time:适用于字段值为时间戳字符,支持范围为[ 1970-01-01 00:00:00, 2038/01/19 03:14:07],毫秒部分会被忽略,time编码适用于 time, datetime, timestamp 等类型;
-
fix_length:适用于超高基场景,将选取字段的前 N 个字节作为编码值,当 N 小于字段长度,会造成字段截断,当 N 较大时,造成 RowKey 过长,查询性能下降,只适用于 varchar 或 nvarchar 类型;
-
fixed_length_hex:适用于字段值为十六进制字符,比如 1A2BFF 或者 FF00FF,每两个字符需要一个字节,只适用于 varchar 或 nvarchar 类型。
-
和Hbase 的RowKey优化类似,在查询的过程中,被用作过滤条件的维度可能放在其他维度的前面,经常出现的维度应该放在前面,基数比较大的维度应该放在前面
(参考链接:https://juejin.im/post/5bd5c59851882565e031f4be) 复制代码
3.2:官方案例维度选择
3.2.1 KYLIN_SALES维度选择遇到的问题
发现连接键没有勾选,如:LEAF_CATEG_ID 和LSTG_SITE_ID是外键,PART_DT 的是衍生外键也没有选择,因此,意味着在CUBE构建时,只用关心计算维度,连接键虽然不选择,衍生维度还是生效的:
3.2.2 KYLIN_CAL_DT 维度选择,发现PART_DT 的是衍生主键没有勾选:
3.2.3 KYLIN_CATEGORY_GROUPINGS维度选择遇到的问题
维度选择,发现连接键LEAF_CATEG_ID和SITE_ID没有选择,那么USER_DEFINED_FIELD1和USER_DEFINED_FIELD3作为衍生列,那么衍生列有什么用呢??看我和我朋友的聊天记录:
3.2.3 BUYER_ACCOUNT 维度选择遇到的问题
发现连接键ACCOUNT_ID没有选择,另外无关列ACCOUNT_SELLER_LEVEL和 ACCOUNT_CONTACT没有被选择,注意ACCOUNT_COUNTRY改名为BUYER_COUNTRY,如下所示:
3.2.4 SELLER_ACCOUNT 维度选择遇到的问题
发现连接键ACCOUNT_ID没有选择,另外无关列ACCOUNT_BUYER_LEVEL和 ACCOUNT_CONTACT没有被选择,注意ACCOUNT_COUNTRY改名为SELLER_COUNTRY,如下所示:
3.2.5 BUYER_COUNTRY 和 SELLER_COUNTRY 维度选择遇到的问题
发现连接键COUNTRY没有选择,另外 NAME改名为BUYER_COUNTRY_NAME和SELLER_COUNTRY_NAME。
3.3 官方案例度量选择
主要的聚合方式有:COUNT、SUM、MIN、MAX、PERCENTILE,下面将详细介绍其他几种聚合方式:
3.3.1 Count Distinct 理论知识:
Apache Kylin提供了两种Count Distinct计算方式,一种是近似的,一种是精确的,精确的Count Distinct指标在Build时候 会消耗更多的资源(内存和存储),Build的过程也比较慢。
近似Count Distinct Apache Kylin使用HyperLogLog算法实现了近似Count Distinct,提供了错误率从9.75%到1.22%几种精度供选择; 算法计算后的Count Distinct指标,理论上,结果最大只有64KB,最低的错误率是1.22%; 这种实现方式用在需要快速计算、节省存储空间,并且能接受错误率的Count Distinct指标计算。
精准Count Distinct Kylin中实现了基于bitmap的精确Count Distinct计算方式。当数据类型为tiny int(byte)、small int(short)以及int, 会直接将数据值映射到bitmap中;当数据类型为long,string或者其他,则需要将数据值以字符串形式编码成dict(字典),再将字典ID映射到bitmap; 指标计算后的结果,并不是计数后的值,而是包含了序列化值的bitmap.这样,才能确保在任意维度上的Count Distinct结果是正确的。 这种实现方式提供了精确的无错误的Count Distinct结果,但是需要更多的存储资源,如果数据中的不重 复值超过百万,结果所占的存储应该会达到几百MB。
3.3.2 EXTEND_COLUMN 理论知识:
Extended Column 在OLAP分析场景中,经常存在对某个id进行过滤,但查询结果要展示为name的情况,比如user_id和user_name。这类问题通常有三种解决方式:
- a. 将ID和Name都设置为维度,查询语句类似select name, count(*) from table where id = 1 group by id,name。这种方式的问题是会导致维度增多,导致预计算结果膨胀;
- b. 将id和name都设置为维度,并且将两者设置为联合。这种方式的好处是保持维度组合数不会增加,但限制了维度的其它优化,比如ID不能再被设置为强制维度或者层次维度;
- c. 将ID设置为维度,Name设置为特殊的Measure,类型为Extended Column。这种方式既能保证过滤id且查询name的需求,同时也不影响id维度的进一步优化。
3.4 Count Distinct和TopN 官方案例讲解:
3.4.1 Count Distinct案例,主要用于对订单去重,错误率要求小于一定百分比
3.4.2 TopN 案例,主要用于按照seller_id 进行分组,然后对price进行聚合操作,实现每一笔订单的销售总额
3.5 cube中Messures总览
4 官方案例Refresh Setting 设置
- Auto Merge Thresholds :自动合并阈值,按天增加的segement,每7天合并一次;7天的segment每28天合并一次
- Retention Threshold:默认为0,保留历史所有的segment。
- Volatile Range: 默认为 0,‘Auto Merge’ 会自动合并所有可能的 cube segments;设置具体的数值后,‘Auto Merge’ 将不会合并最近 Volatile Range 天的 cube segments;假设 Volatile Range 设置为 7,则最近 7 天内生成的 cube segments 不会被自动合并;
- Partition Start Date:分区开始时间
4.1 官方案例Refresh Setting 强制维度设置
和朋友进行反复确认的 强制维度,这里官方案例真的出现了,在进行查询的时候,PART_DT不出现也是可以的,为什么呢?请参考下面的解释
Mandatory 维度指的是那些总是会出现 在Where 条件或 Group By 语句里的维度。当然必须存在不一定是显式出现在查询语句中,例如查询日期是必要字段,月份、季度、年属于它的衍生字段,那么查询的时候出现月份、季度、年这些衍生字段等效于出现查询日期这个必要字段。
4.2 官方案例Refresh Setting层次维度的设置
可以看到官方案例按照层次顺序,进行cubeId的构建,根据绑定关系,进行了剪枝处理
4.3 官方案例Refresh Setting联合维度的设置
根据绑定关系,进行了剪枝处理
4.4 Rokeys 的设置
各维度在 Rowkeys 中的顺序,对于查询的性能会产生较明显的影响;在这里用户可以根据查询的模式和习惯,通过拖曳的方式调整各个维度在Rowkeys上的顺序。推荐的顺序为:Mandatory 维度、where 过滤条件中出现频率较多的维度、高基数维度、低基数维度。这样做的好处是,充分利用过滤条件来缩小在 HBase 中扫描的范围,从而提高查询的效率。
我们发现不常用维度放在了后面:
5 官方案例总览
发现总共使用了20个维度和6个度量,以及6个Lookup Table (包含别名表)
6 官方案例构建CUBE
7 官方案例测试
在进行Cube构建时,我没有选择连接键和衍生维度,那么能不能正常使用,让我们拭目以待,以下是维度剪枝优化的内容,基于此,我们来做测试:
7.1 测试一:Joint Dimension 缺少伙伴是否会报错?
7.1.1 订单事实表与订单类别表的联合查询:
7.1.2 结论:联合维度确实同伴不会报错
7.2 测试二:衍生维度能否正常分组和条件过滤?
7.2.1 订单事实表与时间维度表进行衍生字段分组:
7.2.2 结论:衍生维度分组不会报错
7.2.2 订单事实表与时间维度表进行衍生字段分组+过滤:
7.2.3 结论:衍生维度过滤不会报错
7.3 测试三:Mandatory Dimensions 缺少是否报错?
如果强制维度是衍生维度所在表的连接键(即KYLIN_SALES 与 KYLIN_CAL_DT的连接键CAL_DT)在,那么查询时不包含强制维度也不会不错,参考7.2.1既可以看出来。
7.3.1 结论:KYLIN_SALES.PART_DT缺少不会报错,虽然被设置成强制维度
8 结语
至此 ,整个官方案例剖析完毕,而留给我们的思考又该什么时候结束呢?Kylin作为OLAP技术先驱,真正把HBASE的列式存储功能发挥到了极致,没有HBASE和SPARK作为技术支撑,KYLIN又该是什么呢?如果有机会,我会写一篇HBASE技术专栏,我们来看看Hbase如何基于LSM(Log-Structured Merge Tree)把索引即数据的思想给落地的。如对spark感兴趣请关注我的Spark技术架构剖析专栏。
秦凯新
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 论文总结 - 模型剪枝 Model Pruning
- 深度学习模型剪枝:Slimmable Networks三部曲
- LeetCode37 使用回溯算法实现解数独,详解剪枝优化
- 决策树学习笔记(二):剪枝,ID3,C4.5
- TensorFlow官方发布剪枝优化工具:参数减少80%,精度几乎不变
- 推翻剪枝固有观点?清华、伯克利提出NN过参数化真的不重要
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。