(12)Redis中集合应用场景

集合常见的集合统计模式

聚会统计:多个集合的聚合结果

包括:统计多个集合的共有元素(交集统计);把两个集合相比,统计其中一个集合独有的元素(差集统计);统计多个集合的所有元素(并集统计)

场景

统计某网站每天的新增用户数和第二天的留存用户数

  1. 每天新增用户
    首先定义一个 set 集合用于保存所有登陆过的用户的集合
    在这里插入图片描述
    另外定义一个 set 指定日期当天登录的用户
    在这里插入图片描述

这样统计新增用户数可以使用差集user:id - user:id:日期 就可以得到指定日期新增的用户了

  1. 怎么记录第二天留存用户数?
    此时我们记录两个集合
    user:id:20220415 与set集合user:id:20220416 ,集合中保存是对应日期下当天的登录的用户id,这两个集合进行交集处理,就可以 获得两天都登陆过的用户,也即是第二天留存用户

使用set进行聚合的缺点

上面就是set的聚合操作,但是要注意:
Set 的差集、并集和交集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计算,会导致 Redis 实例阻塞。
你可以从主从集群中选择一个从库,让它专门负责聚合计算,或者是把数据读取到客户端,在客户端来完成聚合统计,这样就可以规避阻塞主库实例和其他从库实例的风险了。

排序统计

在redis的数据类型中,可以进行排序的是ListSorted Set

场景:电商网站上拉去最新评论列表,列表可进行分页

  1. 使用list
    每个商品对应一个 List,这个 List 包含了对这个商品的所有评论,而且会按照评论时间保存这些评论,每来一个新评论,就用 LPUSH 命令把它插入 List的队头。在只有一页评论的时候,我们可以很清晰地看到最新的评论,但是,在实际应用中,网站一般会分页显示最新的评论列表,一旦涉及到分页操作,List 就可能会出现问题了。假设我们有六条评论:abcdef,设每页有3条评论,如果在我们读取了第一页的评论后,系统新增了一天评论g,则原列表成为gabcdef,我们获取第二页评论就成了cde,重复读取了c。之所以会这样,关键原因就在于,List 是通过元素在 List 中的位置来排序的,当有一个新元素插入时,原先的元素在 List 中的位置都后移了一位,比如说原来在第 1 位的元素现在排在了第 2 位。所以,对比新元素插入前后,List 相同位置上的元素就会发生变化,用LRANGE 读取时,就会读到旧元素。

在这里插入图片描述

所以在读取新增评论频繁且有分页的情况下,不建议使用List集合进行排序。这里建议使用Sorted Set进行排序。因为它是根据元素的实际权重来排序和获取数据的
我们可以按评论时间的先后给每条评论设置一个权重值,然后再把评论保存到 Sorted Set中。Sorted Set 的 ZRANGEBYSCORE 命令就可以按权重排序后返回元素。这样的话,即使集合中的元素频繁更新,Sorted Set 也能通过 ZRANGEBYSCORE 命令准确地获取到按序排列的数据。

所以,在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,建议你优先考虑使用 Sorted Set。

二值状态统计

二值状态统计。这里的二值状态就是指集合元素的取值就只有 0 和 1 两种

统计用户打卡签到情况

在签到统计时,每个用户一天的签到用 1 个 bit 位就能表示,一个月(假设是 31 天)的签到情况用 31 个 bit 位就可以,而一年的签到也只需要用 365 个 bit 位,根本不用太复杂的集合类型。这个时候,我们就可以选择 Bitmap。

Bitmap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。String 类型是会保存为二进制的字节数组,所以,Redis 就把字节数组的每个 bit 位利用起来,用来表示一个元素的二值状态。你可以把 Bitmap 看作是一个 bit 数组。Bitmap 提供了 GETBIT/SETBIT操作,使用一个偏移值 offset 对 bit 数组的某一个 bit 位进行读和写。不过,需要注意的是,Bitmap 的偏移量是从 0 开始算的,也就是说 offset的最小值是 0。当使用 SETBIT 对一个 bit 位进行写操作时,这个 bit 位会被设置为 1。Bitmap 还提供了 BITCOUNT 操作,用来统计这个 bit 数组中所有“1”的个数。
bitmap下标是从0开始的。
加入要记录用户8月2日已签到:
SETBIT uid:sign:3000:202008 1 1
检查用户8月2日是否签到:
GETBIT uid:sign:3000:202008 2
统计该用户在8月份的签到次数
BITCOUNT uid:sign:3000:202008

另外一个场景,统计一天中一亿个用户的签到情况。假如我们不使用bitmap,存储一亿的数据还是会占用很大的空间的。所以我们需要使用bitmap来进行存储。我们将日期作为键,bitmap中每个用户作为一个bit位(用户id根据一定的算法映射到bitmap的bit为中),用户签到置位1,否则置位0,示意图如下。

在这里插入图片描述

基数统计

基数统计就是指统计一个集合中不重复的元素个数

应用场景:统计某个页面的UV

方案一:Set

网页 UV 的统计有个独特的地方,就是需要去重,一个用户一天内的多次访问只能算作一次。在 Redis 的集合类型中,Set 类型默认支持去重,所以看到有去重需求时,我们可能第一时间就会想到用 Set 类型。
当需要统计UV时,可以直接使用SCARD命令返回一个集合中的元素。

但是在一个大的项目中,如果需要记录的页面多了,就会使用多个Set,很大程度上会消耗内存。

方案二:Hash

除了使用Set外还可以使用Hash类型进行去重。

你可以把用户 ID 作为 Hash 集合的 key,当用户访问页面时,就用 HSET 命令(用于设置 Hash 集合元素的值),对这个用户 ID 记录一个值“1”,表示一个独立访客,用户 1 访问 page1 后,我们就记录为 1 个独立访客。

HSET page1:uv user1 1

即使用户 1 多次访问页面,重复执行这个 HSET 命令,也只会把 user1 的值设置为 1,仍然只记为 1 个独立访客。当要统计 UV 时,我们可以用 HLEN 命令统计 Hash 集合中的所有元素个数。

但是它和Set集合一样,如果统计的页面过多,就会使用多个Hash,同样消耗redis的内存。

方案三:HyperLogLog

另一个还可以使用的就是HyperLogLog

HyperLogLog 是一种用于统计基数的数据集合类型,它的最大优势就在于,当集合元素数·量非常多时,它计算基数所需的空间总是固定的,而且还很小。

在 Redis 中,每个 HyperLogLog 只需要花费 12 KB 内存,就可以计算接近 2^64 个元素的基数。你看,和元素越多就越耗费内存的 Set 和 Hash 类型相比,HyperLogLog 就非常节省空间。
在统计 UV 时,你可以用 PFADD 命令(用于向 HyperLogLog 中添加新元素)把访问页面的每个用户都添加到 HyperLogLog 中。

 PFADD page1:uv user1 user2 user3 user4 user5

接下来,就可以用 PFCOUNT 命令直接获得 page1 的 UV 值了,这个命令的作用就是返回 HyperLogLog 的统计结果。

PFCOUNT page1:uv

不过,有一点需要你注意一下,HyperLogLog 的统计规则是基于概率完成的,所以它给出的统计结果是有一定误差的,标准误算率是 0.81%。这也就意味着,你使用HyperLogLog 统计的 UV 是 100 万,但实际的 UV 可能是 101 万。虽然误差率不算大,但是,如果你需要精确统计结果的话,最好还是继续用 Set 或 Hash 类型

总结

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值