Apache Kylin中對(duì)上億字符串的精確Count_Distinct示例
如果業(yè)務(wù)中能接受1.22%的誤差,那么肯定首選近似算法,因?yàn)樗芄?jié)省很多資源和時(shí)間。如果業(yè)務(wù)中必須使用精確去重,那么就看看本文的例子(針對(duì)上億字符串的精確去重)。
事實(shí)表
- hive> desc test_t_pbs_uv_fact;
- OK
- ad_id string //維度
- material_id string //維度
- city_code string //維度
- user_id string //指標(biāo),需要精確Count Distinct
- bid_request bigint //指標(biāo),SUM
- device_bid_request bigint //指標(biāo),SUM
- win bigint //指標(biāo),SUM
- ck bigint //指標(biāo),SUM
- pt string //維度,日期,yyyy-MM-dd
該事實(shí)表一天的數(shù)據(jù)記錄大概1.5億+,其中user_id為字符串,類似MD5后的字符串。
創(chuàng)建Model
在Kylin中創(chuàng)建名為lxw1234_uv_model的模型。
選擇維度和指標(biāo)字段:
創(chuàng)建Cube
創(chuàng)建名為lxw1234_uv_cube的Cube,其中,指標(biāo)定義如下:
其他請(qǐng)按實(shí)際業(yè)務(wù)需求配置。
手動(dòng)修改Cube(JSON)
如果不修改,精確Count Distinct使用了Default dictionary來保存編碼后的user_id,而Default dictionary的最大容量為500萬,并且,會(huì)為每個(gè)Segment生成一個(gè)Default dictionary,這樣的話,跨天進(jìn)行UV分析的時(shí)候,便會(huì)產(chǎn)生錯(cuò)誤的結(jié)果,如果每天不重復(fù)的user_id超過500萬,那么build的時(shí)候會(huì)報(bào)錯(cuò):
- 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)
該值由參數(shù) kylin.dictionary.max.cardinality 來控制,當(dāng)然,你可以修改該值為1億,但是Build時(shí)候可能會(huì)因?yàn)閮?nèi)存溢出而導(dǎo)致Kylin Server掛掉:
- # java.lang.OutOfMemoryError: Requested array size exceeds VM limit
- # -XX:OnOutOfMemoryError=”kill -9 %p”
- # Executing /bin/sh -c “kill -9 16193″…
因此,這種需求我們需要手動(dòng)使用Global Dictionary,顧名思義,它是一個(gè)全局的字典,不分Segments,同一個(gè)user_id,在全局字典中只有一個(gè)ID。
目前Kylin的UI中沒有可以直接配置Global Dictionary的地方,需要手動(dòng)修改Cube的JSON描述:
在狀態(tài)為DISABLED的Cube列表中,點(diǎn)擊”Admins”菜單下的”Edit(JSON)”,進(jìn)入Cube JSON描述的編輯頁面,
添加下面的JSON
其中,在override_kylin_properties 中增加了兩個(gè)Cube的配置參數(shù),用于增加Mapper的運(yùn)行內(nèi)存。
- "dictionaries": [
- {
- "column": "USER_ID",
- "builder": "org.apache.kylin.dict.GlobalDictionaryBuilder"
- }
- ]
定義了對(duì)USER_ID字段使用全局字典。
之后,保存JSON。
Build與查詢
Build完成后,在Hive和Kylin中執(zhí)行下面的查詢:
SELECT city_code,SUM(bid_request) AS bid_request,COUNT(DISTINCT user_id) AS uvFROM liuxiaowen.TEST_T_PBS_UV_FACTGROUP BY city_codeORDER BY uv DESC limit 30;
Hive中耗時(shí):181.134 seconds
Kylin中耗時(shí):9 seconds
查詢結(jié)果完全一致:
Global Dictionary存在問題
由于Global Dictionary 底層基于bitmap,其最大容量為Integer.MAX_VALUE,即21億多,如果全局字典中,累計(jì)值超過Integer.MAX_VALUE,那么在Build時(shí)候便會(huì)報(bào)錯(cuò)。
因此,使用全局字典還是有容量的限制。