spark ,hive collect_list全局保持順序

 

Hive中collect_list全局保持順序

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

有以下Hive表的定義:

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, 4

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

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函數替換掉冒號及其前面的數字,大功告成。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章