文章目录
一. 相关面试题
1. 面试题一
- 抖音电商直播,主播介绍商品有评论,1个商品对应一系列评论,排序+展示+取前10条记录?
- 用户在手机APP上的签到打卡信息:1天对应一系列用户的签到记录,新浪微博、钉钉打卡签到,没来如何统计?
- 应用网站傻姑娘的网页浏览信息:1个网页对应一系列的点击访问,淘宝网首页,每天有多少人浏览首页
- 你们公司系统上线后,说一下UV、PV和DAU分别是多少
2. 面试题二
- 记录对集合中的数据进行统计
在移动应用中,需要统计每天的新增用户数和第二天的留存用户数
在电商网站的商品评论中,需要统计评论列表中的最新评论
在签到打卡中,需要统计一个月内连续打卡的用户数
在网页访问中,需要统计独立访客(UV)的量
痛点:
类似于今日头条、抖音、淘宝这样的用户访问级别是亿级的,请问如何处理?
上面问题的关键点就是,对于亿级数据的收集、清洗、统计和展现,如何存、如何取得快,真正有价值的是如何统计
二. 统计的类型
亿级系统中常见的统计有四种:
1. 聚合统计
统计多个集合元素的聚合结果,就是前面讲过的交差并等集合统计
- 集合的差集运算: A − B A-B A−B
属于A但不属于B的元素构成的集合
SDIFF key [key...]
- 集合的并集运算: A ∪ B A \cup B A∪B
属于A或者属于B的元素合并后的集合
SUNION key[key...]
- 集合的交集运算: A ∩ B A \cap B A∩B
SINTER key [key...]
SINTERCARD numkeys key[key ...] [LIMIT limit]
属于A同时也属于B的集
2. 排序统计
例如:抖音段视频最新评论留言的场景,请你设计一个列表。考察你的数据结构和设计思路
对于上面的需求我们可以使用zset
数据结构
3. 二值统计
集合元素的取值就只有0和1两种,在钉钉上班签到打卡的场景中,我们只需要用记录有签到或没签到,此时可以用redis的bitmap数据结构。
4. 基数统计
统计一个集合中不重复的元素个数,这里使用redis的hyperloglog数据结构。
三. Hyperloglog
1. 专业名词
- UV
Unique Visitor,独立访客,一般理解为客户端IP(需要去重考虑)
- PV
Page View,页面访问量(不用去重)
- DAU
Daily Active User,日活跃用户量,登陆或者使用了某个产品的用户数(去除重复登陆的用户),常用于反映网站、互联网应用或者网络游戏运营情况
- MAU
Mouthly Active User,月活跃用户量
很多计数类场景,比如每日注册IP数,每日访问IP数,页面实时访问数,访问用户数,因为主要的目标是高效、巨量的进行计数,所以对存储的数据内容并步关心。
2. Hyperloglog使用
基数:是一种数据集,去重复后的真实个数。
去重复统计功能的基数估计算法就是HyperLogLog,它的优点是,在输入元素数量或者体积非常非常大的时候,计算基数的空间总是固定的,并且很小。在Redis里面,每个HyperLogLog只需要12kb的内存,就可以计算洁净2^64个的不同元素的基数,但是HyperLogLog只会根据输入元素来计算基数,而不会存储输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素。
添加元素到 HyperLogLog:
PFADD key element [element ...]
PFADD my_hyperloglog a b c d e f g
获取 HyperLogLog 的基数估计值:
PFCOUNT key [key ...]
PFCOUNT my_hyperloglog
合并多个 HyperLogLog:
PFMERGE destkey sourcekey [sourcekey ...]
> PFADD hll1 a b c d
(integer) 1
> PFADD hll2 c d e f
(integer) 1
> PFMERGE hll_union hll1 hll2
OK
> PFCOUNT hll_union
(integer) 6
3. Hyperloglog原理
去重的思路:
- hashset:在java中hashset就是一个无重复元素的集合(但是数据量很大不适合)
- bitmap:
bitmap是通过用位bit数组来表示各个元素是否出现,每个元素队员一位,所需的总内存是N个bit。基数技术则嫁给你每一个元素对应到bit数组中的其中一位,新进入的元素只需要讲已经有的bit数组和新加入的元素进行按位或计算就行,这个方式能大大减少内存占用,且操作迅速。例如,假设一个样本案例就是一亿个基数位值数据,一个样本就是一亿,如果要统计1亿个数据的基数位值,大约需要内存1000000000/8/1024约为12M,内存减少占用的效果显著,这样得到的统计一个对象样本的基数值就是12M。但是,统计10000个对象样本(1w个亿级),就需要117.1875G,可见使用bitmaps还是不适合大数据量下(亿级)的基数计数场景。
但是bitmap的统计是精确的不会有误差
- 概率算法:
通过牺牲准确率来换取空间,对于不要求绝对准确的场景下可以使用,因为概率算法不直接存储数据本身,通过一定的概率统计方法预估基数值,同时误差在一定范围内,由于又不存储故此可以大大节约内存。
HyperLogLog就是一种概率算法的体现
HyperLogLog只是进行不重复的基数统计,不是集合也不保存数据,只记录数量而不体现具体内容。它提供一种不精确的去重计数方案,误差大概在0.81%左右。
4. Hyperloglog案例
- 需求
UV的统计需要去重,一个用户一天内的多次访问只能算做一次,淘宝、天猫首页的UV,平均每天是1~1.5亿左右。
- 案例实现
Service
@Service
public class HyperLogLogService {
@Autowired
private RedisTemplate redisTemplate;
@PostConstruct