我用部署的是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函數替換掉冒號及其前面的數字,大功告成。
作者:LittleMagic
鏈接:https://www.jianshu.com/p/3ed003b17f44
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。