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