Redis解读(4):Redis中HyperLongLog、布隆过滤器、限流、Geo、及Scan等进阶应用

Redis中的HyperLogLog

一般我们评估一个网站的访问量,有几个主要的参数:

pv,Page View,网页的浏览量

uv,User View,访问的用户

一般来说,pv 或者 uv 的统计,可以自己来做,也可以借助一些第三方的工具,比如 cnzz,友盟 等。

如果自己实现,pv 比较简单,可以直接通过 Redis 计数器就能实现。但是 uv 就不一样,uv 涉及到另外一个问题,去重。

我们首先需要在前端给每一个用户生成一个唯一 id,无论是登录用户还是未登录用户,都要有一个唯一 id,这个 id 伴随着请求一起到达后端,在后端我们通过 set 集合中的 sadd 命令来存储这个 id,最后通过 scard 统计集合大小,进而得出 uv 数据。

HyperLogLog 问题场景:

例如:CSDN、博客园这种网站首页,或者商城的爆款页面、活动页面。高峰时期都是千万级别的 UV,需要的存储空间就非常惊人,通过Redis中的 Set类型数据结构存储,并不是最佳的解决方案。而且,像 UV 统计这种,一般也不需要特别精确,对于网站服务商来说,800w 的 uv 和 803w 的 uv 数据,其实差别不大。所以,这个场景下,Redis中的 HyperLogLog 就能很好的去解决这个问题。

HyperLogLog 提供了一套不怎么精确但是够用的去重方案,会有误差,官方给出的误差数据是 0.81%,这个精确度,统计 UV 够用了。

HyperLogLog 主要提供了两个命令:pfadd 和 pfcount。

pfadd 用来添加记录,类似于 sadd ,添加过程中,重复的记录会自动去重。

pfcount 则用来统计数据。

127.0.0.1:6379> pfadd uv u1 u2 u3 (integer) 1 127.0.0.1:6379> PFCOUNT uv (integer) 3 127.0.0.1:6379> pfadd uv u1 u2 u3 u3 u2 u1 u4 (integer) 1 127.0.0.1:6379> PFCOUNT uv (integer) 4

目前我们在服务器上统计UV 数据量少的时候看不出来误差。在 Java 中,我们多添加几个元素:

package org.taoguoguo.hyper; import org.taoguoguo.redis.Redis; /** * @author taoguoguo * @description HyperLogLog * @website https://www.cnblogs.com/doondo * @create 2021-04-18 14:10 */ public class HyperLogLog { public static void main(String[] args) { Redis redis = new Redis(); redis.execute(jedis -> { for (int i = 0; i < 1000; i++) { //pfadd uv u0 u1 jedis.pfadd("uv","u"+i,"u"+(i+1)); } long uv = jedis.pfcount("uv"); System.out.println("uv统计值为:" + uv); }); } }

控制台打印值:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See #StaticLoggerBinder for further details. uv统计值为:994 Process finished with exit code 0

理论值是 1001,实际打印出来 994,有误差,但是在可以接受的范围内。

除了 pfadd 和 pfcount 之外,还有一个命令 pfmerge ,合并多个统计结果,在合并的过程中,会自动 去重多个集合中重复的元素。

Redis解读(4):Redis中HyperLongLog、布隆过滤器、限流、Geo、及Scan等进阶应用

布隆过滤器 1.问题场景

我们用 HyperLogLog 来估计一个数,有偏差但是也够用。HyperLogLog 主要提供两个方法:

pfadd

pfcount

但是 HyperLogLog 没有判断是否包含的方法,例如 pfexists 、pfcontains 等。没有这样的方法存在,但是我们有这样的业务需求。

例如刷今日头条,推送的内容有相似的,但是没有重复的,如何将未推送过的内容进行去重推送?50亿个电话号码,清单中的200个已经拉入工信部黑名单,判断是否在这50亿电话号码中。此类场景如何进行过滤统计?

解决方案很多,例如将用户的浏览历史记录下来,然后每次推送时去比较该条消息是否已经给用户推送了。但是这种方式效率极低,不推荐。即便使用缓存,缓存数据量会特别多,效率也非常低,并不适合此类场景。

使用布隆过滤器,就能很好的解决这个问题。

2.Bloom Filter 介绍

Bloom Filter 专门用来解决我们上面所说的去重问题的,使用 Bloom Filter 不会像使用缓存那么浪费空间。当然,他也存在一个小小问题,就是不太精确。

Bloom Filter 相当于是一个不太精确的 set 集合,我们可以利用它里边的 contains 方法去判断某一个对象是否存在,但是需要注意,这个判断不是特别精确。一般来说,通过 contains 判断某个值不存在,那就一定不存在,但是判断某个值存在的话,则他可能不存在。

以今日头条为例,假设我们将用户的浏览记录用 B 表示,A 表示用户没有浏览的新闻,现在要给用户推送消息,先去 B 里边判断这条消息是否已经推送过,如果判断结果说没推送过(B 里边没有这条记录),那就一定没有推送过。如果判断结果说有推送(B 里边也有可能没有这条消息),这个时候该条消息就不会推送给用户,导致用户错过该条消息,当然这是概率极低的。

3.Bloom Filter 原理

每一个布隆过滤器,在 Redis 中都对应了一个大型的位数组以及几个不同的 hash 函数(无偏hash)。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzdxwg.html