微信搜索lxw1234bigdata | 邀请体验:数阅–数据管理、OLAP分析与可视化平台 | 赞助作者:赞助作者

Apache Kylin中对上亿字符串的精确Count_Distinct示例

Kylin lxw1234@qq.com 19635℃ 6评论

上篇文章《在Apache Kylin中使用Count Distinct》中介绍了Apache Kylin中Count Distinct的实现,如果业务中能接受1.22%的误差,那么肯定首选近似算法,因为它能节省很多资源和时间。如果业务中必须使用精确去重,那么就看看本文的例子(针对上亿字符串的精确去重)。

事实表

hive> desc test_t_pbs_uv_fact;
OK
ad_id                   string  //维度
material_id             string   //维度
city_code               string  //维度
user_id                 string   //指标,需要精确Count Distinct
bid_request             bigint 	//指标,SUM
device_bid_request      bigint		//指标,SUM
win                     bigint	//指标,SUM 
ck                      bigint	//指标,SUM 
pt                      string	//维度,日期,yyyy-MM-dd

该事实表一天的数据记录大概1.5亿+,其中user_id为字符串,类似MD5后的字符串。

创建Model

在Kylin中创建名为lxw1234_uv_model的模型。

选择维度和指标字段:

kylin

 

kylin

创建Cube

创建名为lxw1234_uv_cube的Cube,其中,指标定义如下:

kylin

 

kylin

其他请按实际业务需求配置。

手动修改Cube(JSON)

如果不修改,精确Count Distinct使用了Default dictionary来保存编码后的user_id,而Default dictionary的最大容量为500万,并且,会为每个Segment生成一个Default dictionary,这样的话,跨天进行UV分析的时候,便会产生错误的结果,如果每天不重复的user_id超过500万,那么build的时候会报错:

java.lang.IllegalArgumentException: Too high cardinality is not suitable for dictionary — cardinality: 43377845
at org.apache.kylin.dict.DictionaryGenerator.buildDictionary(DictionaryGenerator.java:96)
at org.apache.kylin.dict.DictionaryGenerator.buildDictionary(DictionaryGenerator.java:73)

该值由参数 kylin.dictionary.max.cardinality 来控制,当然,你可以修改该值为1亿,但是Build时候可能会因为内存溢出而导致Kylin Server挂掉:

# java.lang.OutOfMemoryError: Requested array size exceeds VM limit
# -XX:OnOutOfMemoryError=”kill -9 %p”
#   Executing /bin/sh -c “kill -9 16193″…

因此,这种需求我们需要手动使用Global Dictionary,顾名思义,它是一个全局的字典,不分Segments,同一个user_id,在全局字典中只有一个ID。

目前Kylin的UI中没有可以直接配置Global Dictionary的地方,需要手动修改Cube的JSON描述:

kylin

在状态为DISABLED的Cube列表中,点击”Admins”菜单下的”Edit(JSON)”,进入Cube JSON描述的编辑页面,

添加下面的JSON:

kylin

 

其中,在override_kylin_properties 中增加了两个Cube的配置参数,用于增加Mapper的运行内存。

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

定义了对USER_ID字段使用全局字典。

之后,保存JSON。

Build与查询

Build完成后,在Hive和Kylin中执行下面的查询:

SELECT city_code,
SUM(bid_request) AS bid_request,
COUNT(DISTINCT user_id) AS uv
FROM liuxiaowen.TEST_T_PBS_UV_FACT
GROUP BY city_code
ORDER BY uv DESC limit 30;

Hive中耗时:181.134 seconds

Kylin中耗时:9 seconds

查询结果完全一致:

kylin

kylin

Global Dictionary存在问题

由于Global Dictionary 底层基于bitmap,其最大容量为Integer.MAX_VALUE,即21亿多,如果全局字典中,累计值超过Integer.MAX_VALUE,那么在Build时候便会报错。
因此,使用全局字典还是有容量的限制。

 

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

转载请注明:lxw的大数据田地 » Apache Kylin中对上亿字符串的精确Count_Distinct示例

喜欢 (23)
分享 (0)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(6)个小伙伴在吐槽
  1. Global Dictionary 这个是针对Measure 中的count_distinct 的吧?假设把user_id放到维度中,会报 Too high cardinality 这个错误吗?
    Apache kylin2016-09-01 17:21 回复
    • user_id是作为指标字段的,放在维度中不行,会报你说的这个错误。
      lxw1234@qq.com2016-09-02 10:26 回复
  2. count_distinct精确去重,不是只支持整数类型吗?而你的字段user_id为字符串也支持?
    BigData2016-10-16 22:05 回复
    • 1.5.3开始支持字符串。
      lxw1234@qq.com2016-10-17 08:44 回复
  3. kylin1.6 ,measure 配置count_distinct,有配置全局字典的gui,但是build第一次正常,build下一个segment的时候会报错java.lang.RuntimeException: Failed to create dictionary on XXXX
    readme2017-01-10 09:35 回复
  4. Integer.MAX_VALUE虽然是21亿多,但是也可以对负数进行去重,所以应该最大可以达到42亿
    hww2019-10-11 10:57 回复