背景:
計算同一品類兩兩商品的相似度,已有的數據結構:[(cid,int); (pid,int); (features,vector)],商品數4W,商品對8W,用時8h。分析是由於數據傾斜導致,例如cid1有100個商品,cid2有300個商品,cid3有1000個商品,由於根據分類id,計算商品相似度,cid3的商品對在一個task中,導致所有任務都等待這一個task運行結束。
優化方案:
1、優化數據通信時間消耗
商品特徵向量是1024維的向量,大約5K,4W商品共200M,可以將圖片的特徵向量進行廣播,在每個執行器executor中保存一份,減少數據通信開銷;否則按每個商品計算1000個商品對,總數據通信量爲4W*5M=200G,在實際計算中遠遠超過200G。
2、優化數據傾斜
對連接後的商品對,按照(pid1,pid2)對做repartition哈希分區,因爲商品對是唯一的,所以根據商品對做shuffle運算後,相似度計算均勻分佈到每個task中,消除數據傾斜的問題。
優化後運行時間爲1h30min。
代碼如下:
#對商品的特徵向量進行特徵向量,優化數據通信時間開銷
feature_broadcast = fn.broadcast(feature_vector)
#根據品類id,進行自連接,對商品對進行分區shuffle操作,優化數據傾斜
pid_pairs = cid_pid.join(cid_pid, "cid")\
.toDF("cid", "pid1", "pid2")\
.repartition(100, "pid1", "pid2").cache()
#獲取商品特徵信息,計算相似度
@fn.udf(returnType=FloatType())
def cos_sim(a, b):
#將計算結果轉化爲float類型,因爲string類型佔有內存(char類型佔2B)
return float(a.dot(b)/(a.norm(2) * b.norm(2)))
pid_pairs_feature = pid_pairs.join(feature_broadcast, feature_broadcast.products_id == pid_pairs.pid1)\
.select("cid", "pid1", "pid2", feature_broadcast.features.alias("feature1"))\
.join(feature_broadcast, feature_broadcast.products_id == pid_pairs.pid2)\
.select("cid", "pid1", "pid2", "feature1", feature_broadcast.features.alias("feature2"))
pid_pairs_simscore = pid_pairs_feature.withColumn("simscore", cos_sim("feature1", "feature2"))\
.select("pid1", "pid2", "simscore")