Redis案例实战之Bitmap、Hyperloglog、GEO
经典面试题
面试题1
抖音电商直播,主播介绍的商品有评论,一个商品对应了一系列的评论,排序+展现+取前10条记录
用户在手机APP上的签到打卡信息:一天对应一系列用户的签到记录,新浪微博、钉钉签到,来没来如何统计?
应用网站上的网页访问信息:一个网页对应一系列的访问点击,淘宝网首页,每天有多少人浏览首页?
你们公司系统上线后,说一下UV、PV、DAU分别是多少?
面试题2
面试问
记录对集合中的数据进行统计
在移动应用中,需要统计每天的新增用户数和第2天的留存用户数;
在电商网站的商品评论中,需要统计评论列表中的最新评论;
在签到打卡中,需要统计一个月内连续打卡的用户数;
在网页访问记录中,需要统计独立访客(Unique Visitor,UV)量。
痛点:类似今日头条、抖音、淘宝这样的额用户访问级别都是亿级的,请问如何处理?
需求痛点
亿级数据的收集+清洗+统计+展现
一句话:存的进+取得快+多维度
真正有价值的是统计…
统计的类型有哪些?
亿级系统中常见的四种统计
聚合统计
统计多个集合元素的聚合结果,就是前面讲解过的交差并等集合统计
排序统计
抖音短视频最新评论留言的场景,请你设计一个展现列表。这个主要考察的就是数据结构和设计思路
设计思路
以抖音vcr最新的留言评价为案例,所有评论需要两个功能,按照时间排序(正序、反序)+分页显示
能够排序+分页显示的redis数据结构是什么合适?
在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示的话,建议使用Zset
按照时间戳进行排列即可。
二值统计
集合元素的取值就只有0和1两种。
在钉钉上班签到打卡的场景中,我们只用记录有签到(1)或没签到(0)特指bigmap
基数统计
指统计一个集合中不重复的元素个数,特指hyperloglog
Hyperloglog
行业术语
什么是UV
Unique Visitor 独立访客,一般理解为客户单ip,一般需要做去重考虑。
什么是PV
Page View 页面浏览量,不需要考虑去重
什么是DAU
Daily Active User 日活跃用户量:登陆或者使用了某个产品的用户数(去掉重复登陆的用户)
常用于反应网站、互联网应用或者网络游戏的运营情况
什么是MAU
Monthly Active User:月活跃用户量
需求
很多计数类场景,比如 每日注册 IP 数、每日访问 IP 数、页面实时访问数 PV、访问用户数 UV等。
因为主要的目标高效、巨量地进行计数,所以对存储的数据的内容并不太关心。
也就是说它只能用于统计巨量数量,不太涉及具体的统计对象的内容和精准性。
统计单日一个页面的访问量(PV),单次访问就算一次。
统计单日一个页面的用户访问量(UV),即按照用户为维度计算,单个用户一天内多次访问也只算一次。
多个key的合并统计,某个门户网站的所有模块的PV聚合统计就是整个网站的总PV。
Hyperloglog是什么?
基数
是一种数据集,去重复后的真实个数
去重复统计功能的基数估计算法,就是Hyperloglog
基数统计
用于统计一个集合中不重复的元素个数,就是对集合去重复后剩余元素的计算
总之就是去重脱水后的真实数据
基本命令
原理
如果说去重统计你会先想到那些方式呢?
HashSet
bitmap
如果数据显较大亿级统计,使用bitmaps同样会有这个问题。
bitmap是通过用位bit数组来表示各元素是否出现,每个元素对应一位,所需的总内存为N个bit。
基数计数则将每一个元素对应到bit数组中的其中一位,比如bit数组010010101(按照从零开始下标,有的就是1、4、6、8)。
新进入的元素只需要将已经有的bit数组和新加入的元素进行按位或计算就行。这个方式能大大减少内存占用且位操作迅速。
But,假设一个样本案例就是一亿个基数位值数据,一个样本就是一亿
如果要统计1亿个数据的基数位值,大约需要内存100000000/8/1024/1024约等于12M,内存减少占用的效果显著。
这样得到统计一个对象样本的基数值需要12M。
如果统计10000个对象样本(1w个亿级),就需要117.1875G将近120G,可见使用bitmaps还是不适用大数据量下(亿级)的基数计数场景,
但是bitmaps方法是精确计算的。
结论
样本元素越多,内存消耗急剧增大,难以管控+各种慢,对于亿级统计不太适合,大数据害死人,简单说就是量变引起质变。
方法
通过牺牲准确率来换取空间,对于不要求绝对准确率的场景下可以使用,因为概率算法不直接存储数据本身,通过一定的概率统计方法预估基数值,同时保证误差在一定范围内,由于又不储存数据故此可以大大节约内存。
HyperLogLog就是一种概率算法的实现。
原理
其本质就只是进行不重复的技术统计,不是集合也不保存数据,只记录数量而不是具体内容。
但是缺点就是有误差,其提供不准确的去重计数方案,牺牲准确率来换取空间,官网上说误差仅仅只是0.81%左右。
这个误差如何得来的呢?
淘宝网站首页亿级UV的Redis统计方案
需求
UV的统计需要去重,一个用户一天内的多次访问只能算作一次
淘宝、天猫首页的UV,平均每天是1~1.5亿左右
每天存1.5亿的IP,访问者来了后先去查是否存在,不存在就加入
方案设计
mysql
数据量太大,且没意义
redis的hash结构存储
redis——hash = >
按照ipv4的结构来说明,每个ipv4的地址最多是15个字节(ip = “192.168.111.1”,最多xxx.xxx.xxx.xxx)
某一天的1.5亿 * 15个字节= 2G,一个月60G,redis死定了。
hyperloglog
HyperLogLogService
@Service @Slf4j public class HyperLogLogService { @Resource private RedisTemplate redisTemplate; /** * 模拟后台有用户点击首页,每个用户来自不同ip地址 */ @PostConstruct public void init() { log.info("------模拟后台有用户点击首页,每个用户来自不同ip地址"); new Thread(() -> { String ip = null; for (int i = 1; i <=200; i++) { Random r = new Random(); ip = r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256); Long hll = redisTemplate.opsForHyperLogLog().add("hll", ip); log.info("ip={},该ip地址访问首页的次数={}",ip,hll); //暂停几秒钟线程 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } },"t1").start(); } }
Redis系列-8.Redis案例实战之Bitmap、Hyperloglog、GEO(下):https://developer.aliyun.com/article/1414711