• 欢迎访问蜷缩的蜗牛博客 蜷缩的蜗牛
  • 微信搜索: 蜷缩的蜗牛 | 联系站长 kbsonlong@qq.com
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

在Apache Kylin中使用Count Distinct

大数据 lxw1234@qq.com 2年前 (2016-08-25) 92次浏览 已收录

在 OLAP 多维分析中,Count Distinct(去重计数)是一种非常常用的指标度量,比如一段时间内的 UV、活跃用户数等等;
从 1.5.3 开始,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

从 1.5.3 版本开始,Kylin 中实现了基于 bitmap 的精确 Count Distinct 计算方式。当数据类型为 tiny int(byte)、small int(short)以及 int,
会直接将数据值映射到 bitmap 中;当数据类型为 long,string 或者其他,则需要将数据值以字符串形式编码成 dict(字典),再将字典 ID 映射到 bitmap;

 

指标计算后的结果,并不是计数后的值,而是包含了序列化值的 bitmap.这样,才能确保在任意维度上的 Count Distinct 结果是正确的。
这种实现方式提供了精确的无错误的 Count Distinct 结果,但是需要更多的存储资源,如果数据中的不重复值超过百万,结果所占的存储应该会达到几百 MB。

全局字典(Global Dictionary)

默认情况下,Kylin 在每个 Segment 中,将数据值编码到一个字典中,同一个数据值,在不同 Segment 中编码后的 ID 值也是不同的,
因此在这跨两个 Segment 中进行 Count Distinct 计算时候,结果是不正确的;

 

在 1.5.3 版本中,Kylin 引进了”Global Dictionary”,用来确保同一个数据值编码后的 ID 值始终是相同的,与此同时,字典的容量也进行了扩充,
一个字典的最大容量达到了 20 亿,之前默认的字典最大容量为 500 万。全局字典可以代替之前的默认字典。

当前版本 1.5.3 的 UI 中,没有提供定义全局字典的地方,需要手动修改 Cube 的 json:

"dictionaries": [
    {
       "column": "SUCPAY_USERID",
	 	   "reuse": "USER_ID",
       "builder": "org.apache.kylin.dict.GlobalDictionaryBuilder"
    }
]

“column”是需要编码(进行 Count Distinct 计算)的字段,”builder”指定了字典的 builder 类,目前只能是”org.apache.kylin.dict.GlobalDictionaryBuilder”.
“reuse”是用来优化字典的,当多个字段的值是同一个数据集的时候,指定复用同一个字典即可,不需要再建立字典,后面会详细说明。

全局字典不能用在维度的编码中,如果一个字段即是维度,又是 Count Distinct 指标,那么就需要为维度指定其他的编码方式。

Example

| DT         | USER_ID | FLAG1 | FLAG2 | USER_ID_FLAG1 | USER_ID_FLAG2 |
| :———-:     | :——:    | :—:   | :—:   | :————-:       | :————-: |
| 2016-06-08 | AAA     | 1     | 1     | AAA           | AAA |
| 2016-06-08 | BBB     | 1     | 1     | BBB           | BBB |
| 2016-06-08 | CCC     | 0     | 1     | NULL          | CCC |
| 2016-06-09 | AAA     | 0     | 1     | NULL          | AAA |
| 2016-06-09 | CCC     | 1     | 0     | CCC           | NULL |
| 2016-06-10 | BBB     | 0     | 1     | NULL          | BBB |

表中有几个基础列:DT、USER_ID、FLAG1、FLAG2;
还有两个条件列:USER_ID_FLAG1=if(FLAG1=1,USER_ID,null)、USER_ID_FLAG2=if(FLAG2=1,USER_ID,null);假设 Cube 按天 Build,有 3 个 Segments。

如果不使用全局字典,在一个 Segment(一天数据)中计算 Count Distinct 是准确的,但是如果在两个 Segment 中计算 Count Distinct,结果是错误的

select count(distinct user_id_flag1) from lxw1234 where dt in ('2016-06-08', '2016-06-09')

这个查询结果是将会是 2,而不是 3, 因为在 Segment 2016-06-08 的字典中,AAA 的编码 ID 为 1,BBB 的编码 ID 为 2,而在 Segment 2016-06-09 中,CCC 的编码 ID 也为 1,因此,编码 ID 去重后,结果是 2.

如果使用下面的方式配置了全局字典,那么这三个值的编码 ID 为: AAA=>1, BBB=>2, CCC=>3,去重后的结果是正确的 3。

“dictionaries”: [ { “column”: “USER_ID_FLAG1″, “builder”: “org.apache.kylin.dict.GlobalDictionaryBuilder” } ]

事实上,USER_ID_FLAG1 和 USER_ID_FLAG2 都是 USER_ID 的子集,这时候,只需要使用 USER_ID 来创建全局字典,那么另外两个,其实可以复用的,这就是前面提到的 reuse。

"dictionaries": [ 
		{ "column": "USER_ID", "builder": "org.apache.kylin.dict.GlobalDictionaryBuilder" }, 
		{ "column": "USER_ID_FLAG1", "reuse": "USER_ID", "builder": "org.apache.kylin.dict.GlobalDictionaryBuilder" }, 
		{ "column": "USER_ID_FLAG2", "reuse": "USER_ID", "builder": "org.apache.kylin.dict.GlobalDictionaryBuilder" } 
] 

性能优化

全局字典是比较大的,在 Build 时候,”Build Base Cuboid Data”这一步会消耗较长时间。
如果字典大小超过 Mapper 的内存大小时候,字典需要消耗大量时间在缓存加载和回收上,解决该问题的办法是修改 Cube 的参数,适当增大 Mapper 使用的内存:
kylin.job.mr.config.override.mapred.map.child.java.opts=-Xmx8g
kylin.job.mr.config.override.mapreduce.map.memory.mb=8500

总结

选择哪种 Count Distinct 计算方式呢?
1. 如果能接受 1.22%以内的误差,近似计算肯定是最好的方式;
2. 如果业务需要精确去重计数,那么肯定得选择精确 Count Distinct;
3. 如果不需要跨 Segment(天)的去重,或者字段值是 tinyint/smallint/int, 或者字段去重后的值小于 500 万,那么就是用默认字典;
否则,就需要配置全局字典,同时,如果可以,则是用”reuse”来进行优化。

下一篇文章中,将介绍使用精确 Count Distinct 对 1.5 亿 string 类型数据的精确去重示例。

 

本文翻译自:http://kylin.apache.org/blog/2016/08/01/count-distinct-in-kylin/

未经同意,不得转载。

 

如果觉得本博客对您有帮助,请 赞助作者

转载请注明:lxw 的大数据田地 » 在 Apache Kylin 中使用 Count Distinct


蜷缩的蜗牛 , 版权所有丨如未注明 , 均为原创丨 转载请注明在 Apache Kylin 中使用 Count Distinct
喜欢 (0)
[]
分享 (0)