Hive中collect_list全局保持順序

我用部署的是standalone模式,local單節點計算的時候,結果沒問題,當集羣計算的時候由於是分佈式的,所以結果是亂序的。解決方法以下:sql

有如下Hive表的定義:app

create table topic_recommend_score ( category_id int, topic_id bigint, score double, rank int ); 

這張表是咱們業務裏話題推薦分值表的簡化版本。category_id表明分類ID,topic_id是話題ID,score是評分值。rank表明每一個分類下話題分值的排名,用開窗函數計算出來的:
row_number() over(partition by t.category_id order by t.score desc)分佈式

在對外提供推薦結果時,咱們會將每一個小組下排名前1000的話題ID取出,拼成一個逗號分隔的字符串,處理以後送入HBase供調用方查詢。拼合的SQL語句以下:函數

select category_id, concat_ws(',',collect_list(cast(topic_id as string))) from topic_recommend_score where rank >= 1 and rank <= 1000 group by category_id; 

看起來沒什麼問題?但其實是錯誤的。輸出結果中總會有一些category_id對應的列表順序異常,好比原本排名正數與排名倒數的兩批ID調換了位置,即rank變成了n-3, n-2, n-1, n, 5, 6, 7, ..., n-4, 1, 2, 3, 4spa

產生這個問題的根本緣由天然在MapReduce,若是啓動了多於一個mapper/reducer來處理數據,select出來的數據順序就幾乎確定與原始順序不一樣了。考慮把mapper數固定成1比較麻煩(見我以前寫的那篇Hive調優文章),也不現實,因此要迂迴地解決問題:把rank加進來再進行一次排序,拼接完以後把rank去掉。以下:code

select category_id, regexp_replace( concat_ws(',', sort_array( collect_list( concat_ws(':',lpad(cast(rank as string),5,'0'),cast(topic_id as string)) ) ) ), '\\d+\:','') from topic_recommend_score where rank >= 1 and rank <= 1000 group by category_id; 

這裏將rank放在了topic_id以前,用冒號分隔,而後用sort_array函數對collect_list以後的結果進行排序(只支持升序)。特別注意,rank必需要在高位補足夠的0對齊,由於排序的是字符串而不是數字,若是不補0的話,按字典序排序就會變成1, 10, 11, 12, 13, 2, 3, 4...,又不對了。
將排序的結果拼起來以後,用regexp_replace函數替換掉冒號及其前面的數字,大功告成。regexp

做者:LittleMagic 連接:https://www.jianshu.com/p/3ed003b17f44 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
相關文章
相關標籤/搜索