bitmap方法是数据压缩使用的常用算法,当字段有明确上下界的时候,使用位图模式来减少存储。在业务指标体系中特别适合通用型留存指标的计算。
我们常写的留存率指标:次留/3留/7留/15留/30留,实际上都是一个公式:
第N日留存率公式 = (T日访问用户)∩ (T+N日访问用户) / (T日访问用户)
那么一般情况下,我们怎么写呢:
select user_id from 访问表 where ds=T 交集
select user_id from 访问表 where ds=T+N 除以
select user_id from 访问表 where ds=T
设计也很简单,每一个bit位代表每一天的访问,最低位代表今天;
比如 0001 就代表最近4天只有今天来访,0101 就代表最近4天有2天来访问,分别是今天和前天
bitmap访问分布 = sum(今日访问 UNION ALL 左移位(昨日访问表))
特别注意:对bitmap做了上限控制,vst_bit_31d&(pow(2,N)-1) 防止溢出。2147483647=pow(2,31)-1表示最近31天的数据
select user_id from B WHERE ds = T and vst_bit_31d & K1 = K1 -- 当日和7日前来访
交集
select user_id from B WHERE ds = T and vst_bit_31d & K2 = K2 -- 7日前来访
除以
select user_id from B WHERE ds = T and vst_bit_31d & K2 = K2 -- 7日前来访
select user_id from B WHERE ds = T and vst_bit_31d & K1 > K2 -- 7日前来访 且后面7日中有一天来访就算
交集
select user_id from B WHERE ds = T and vst_bit_31d & K2 = K2
除以
select user_id from B WHERE ds = T and vst_bit_31d & K2 = K2
select product_id -- 业务id
, visitor_id -- 访问者id
, sum(vst_bit_31d) -- 最近31天的访问bitmap
FROM
(
-- 昨日分区bit左移1位
SELECT product_id
,visitor_id
,IF(vst_bit_31d > 0, SHIFTLEFT(vst_bit_31d, 1)&2147483647, 0 ) as vst_bit_31d
FROM ${your_bitmap_table_name} -- Bitmap表
WHERE ds = '${sub1d}'
UNION ALL
-- 今日访问bit=1
SELECT product_id
,visitor_id
,1
FROM ${your_visit_table_name} -- 访问表
WHERE ds = '${bizdate}'
group by product_id, visitor_id
)
GROUP by product_id, visitor_id
需要注意的是product_id 为最细粒度,不可跨粒度计算
@vst_bit_2 :=
SELECT product_id, count(visitor_id) as uv
FROM ${your_bitmap_table_name} -- Bitmap表
WHERE ds = '${bizdate}'
AND vst_bit_31d & 2 = 2 -- 昨日访问UV, 分母
AND ( '${your condition}' )
GROUP BY product_id
;
@vst_bit_3 :=
SELECT product_id, count(visitor_id) as uv
FROM ${your_bitmap_table_name} -- Bitmap表
WHERE ds = '${bizdate}'
AND vst_bit_31d & 3 = 3 -- 昨日留存UV, 分子
AND ( '${your condition}' )
GROUP BY product_id
;
-- 计算次留率
select product_id, min(uv)/max(uv) 次留率
(
select * FROM @vst_bit_3
UNION ALL
select * FROM @vst_bit_2
)
group by product_id;
-- 最近7天内的访问天数,其中127=pow(2,7)-1=127
SELECT product_id
, visitor_id
, bi_udf:bi_bit_count(vst_bit_31d&127, 1) AS vst_days
FROM ${your_bitmap_table_name} -- Bitmap表
WHERE ds = '${bizdate}'
AND ( '${your condition}' )
GROUP BY product_id, visitor_id, bi_udf:bi_bit_count(vst_bit_31d&127, 1)
;
● bitmap的使用确实可以加速数据的运算,每天存储的是之前30天是否来访,
● 如果想看过去60天的,也方便进行扩展
○ 可拿ds-30的分区数据,拿到过去30-60天的访问数据
○ 或者把这个vst_bit_31d变成longlong类型
● 但是使用时需要注意,这个建立B表的时候,维度一定要确定不能组合,比较死板
● 新建表后需要按时间顺序回刷