本文為您介紹如何對長周期指標的計算進行優化。
實驗背景
電子商務公司在電商數據倉庫和商業分析場景中,經常需要計算最近N天的訪客數、購買用戶數、老客數等類似的指標。這些指標需要根據一段時間內的累積數據進行計算。
通常,這些指標的計算方式為從日志明細表中查詢數據進行計算。例如,運行如下SQL語句計算商品最近30天的訪客數。
SELECT item_id --商品id
,COUNT(DISTINCT visitor_id) AS ipv_uv_1d_001
FROM 用戶訪問商品日志明細表
WHERE ds <= ${bdp.system.bizdate}
AND ds >=to_char(dateadd(to_date(${bdp.system.bizdate},'yyyymmdd'),-29,'dd'),'yyyymmdd')
GROUP BY item_id;
代碼中的變量都是DataWorks的調度變量,僅適用于DataWorks的調度任務。下文不再重復說明。
當每天的日志量很大時,SELECT操作需要大量的Map Instance,運行上面的代碼需要的Map Instance個數太多,甚至會超過99999個Instance的限制個數,導致Map Task無法順利執行。
實驗目的
在不影響性能的情況下計算長周期的指標。
影響性能的根源是多天匯總數據量過大,建議您使用構建臨時表的方式對每天的數據進行輕度匯總,這樣可以去掉很多重復數據,減少數據量。
實驗方案
構建中間表,每天匯總一次。
對于上述示例,構建
item_id+visitior_id
粒度的日匯總表,記作A。INSERT OVERWRITE TABLE mds_itm_vsr_xx(ds='${bdp.system.bizdate} ') SELECT item_id,visitor_id,count(1) AS pv FROM ( SELECT item_id,visitor_id FROM 用戶訪問商品日志明細表 WHERE ds =${bdp.system.bizdate} GROUP BY item_id,visitor_id ) a;
計算多天的數據,依賴中間表進行匯總。
對A進行30天的匯總。
SELECT item_id ,COUNT(DISTINCT visitor_id) AS uv ,SUM(pv) AS pv FROM mds_itm_vsr_xx WHERE ds <= '${bdp.system.bizdate} ' AND ds >= to_char(dateadd(to_date('${bdp.system.bizdate} ','yyyymmdd'),-29,'dd'),'yyyymmdd') GROUP BY item_id;
影響及思考
上述方法對每天的訪問日志明細數據進行單天去重,從而減少了數據量,提高了性能。缺點是每次計算多天數據的時候,都需要讀取N個分區的數據。
您可以通過增量累計方式計算長周期指標,不需要讀取N個分區的數據,而是把N個分區的數據壓縮合并成一個分區的數據,讓一個分區的數據包含歷史數據的信息。
場景示例
計算最近1天店鋪商品的老買家數。老買家是指過去一段時間購買過商品的買家(例如過去30天)。
一般情況下,老買家數計算方式如下所示。
SELECT item_id --商品id
,buyer_id AS old_buyer_id
FROM 用戶購買商品明細表
WHERE ds < ${bdp.system.bizdate}
AND ds >=to_char(dateadd(to_date(${bdp.system.bizdate},'yyyymmdd'),-29,'dd'),'yyyymmdd')
GROUP BY item_id
,buyer_id;
改進思路:
維護一張店鋪商品和買家購買關系的維表A,記錄買家和店鋪的購買關系、第一次購買時間、最近一次購買時間、累計購買件數、累計購買金額等信息。
每天使用最近1天的支付明細日志更新表A的相關數據。
計算老買家數量時,判斷最近一次購買時間是否在30天之內,從而最大程度上的數據關系對去重,減少計算輸入數據量。