本文为您介绍如何对长周期指标的计算进行优化。
实验背景
电子商务公司在电商数据仓库和商业分析场景中,经常需要计算最近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 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天之内,从而最大程度上的数据关系对去重,减少计算输入数据量。