本文主要是介绍12 | 有一亿个keys要统计,应该用哪种集合?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- Redis核心技术与实战
- 实践篇
- 12 | 有一亿个keys要统计,应该用哪种集合?
- 聚合统计
- 排序统计
- 二值状态统计
- 基数统计
- Redis 集合类型比较
Redis核心技术与实战
实践篇
12 | 有一亿个keys要统计,应该用哪种集合?
要想选择合适的集合,就得了解常用的集合统计模式。集合类型常见的四种统计模式,包括聚合统计、排序统计、二值状态统计和基数统计。
聚合统计
聚合统计,就是指统计多个集合元素的聚合结果,包括:统计多个集合的共有元素(交集统计);把两个集合相比,统计其中一个集合独有的元素(差集统计);统计多个集合的所有元素(并集统计)。
应用场景: 统计手机 App 每天的新增用户数和第二天的留存用户数
解决方案: 用一个集合记录所有登录过 App 的用户 ID,同时,用另一个集合记录每一天登录过 App 的用户 ID。然后,再对这两个集合做聚合统计。
记录所有登录过 App 的用户 ID 可以直接使用 Set 类型,把 key 设置为 user:id,表示记录的是用户 ID,value 就是一个 Set 集合,里面是所有登录过 App 的用户 ID,把这个 Set 叫作累计用户 Set,如下图所示:
接统计每天的新增用户,key 是 user:id 以及当天日期,例如 user : id :20200803,value 是 Set 集合,记录当天登录的用户 ID。如下图所示:
假设手机 App 在 2020 年 8 月 3 日上线,那么,8 月 3 日前是没有用户的。此时,累计用户 Set 是空集,当天登录的用户 ID 会被记录到 key 为 user : id: 20200803 的 Set 中。所以,user : id : 20200803 这个 Set 中的用户就是当天的新增用户。
然后,计算累计用户 Set 和 user : id : 20200803 Set 的并集(SUNIONSTORE) 结果,结果保存在 user:id 这个累计用户 Set 中,如下所示:
SUNIONSTORE user:id user:id user:id:20200803
等到 8 月 4 日再统计时,把 8 月 4 日登录的用户 ID 记录到 user : id: 20200804 的 Set 中。接下来,计算累计用户 Set 和 user : id : 20200804 Set 的差集(SDIFFSTORE),结果保存在 key 为 user:new 的 Set 中,如下所示:
SDIFFSTORE user:new user:id:20200804 user:id
计算 8 月 4 日的留存用户时,只需要再计算 user : id : 20200803 和 user : id : 20200804 两个 Set 的交集(SINTERSTORE)。
Set 的差集、并集和交集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计算,会导致 Redis 实例阻塞。所以,可以从主从集群中选择一个从库,让它专门负责聚合计算,或者是把数据读取到客户端,在客户端来完成聚合统计,这样可以规避阻塞主库实例和其他从库实例的风险。
排序统计
应用场景: 电商网站上提供最新评论列表
解决方案: 要求集合类型能对元素保序
在 Redis 常用的 4 个集合类型中(List、Hash、Set、Sorted Set),List 和 Sorted Set 属于有序集合。
List 是按照元素进入 List 的顺序进行排序,而 Sorted Set 可以根据元素的权重来排序。
- 采用 List 的情况。每个商品对应一个 List,这个 List 包含了对这个商品的所有评论,而且会按照评论时间保存这些评论,每来一个新评论,就用 LPUSH 命令把它插入 List 的队头。
一旦涉及到分页操作,List 就可能会出现问题。 原因在于,List 是通过元素在 List 中的位置来排序的,当有一个新元素插入时,原先的元素在 List 中的位置都后移了一位。所以,对比新元素插入前后,List 相同位置上的元素就会发生变化,用 LRANGE 读取时,就会读到旧元素。 - 采用 Sorted Set 的情况。可以按评论时间的先后给每条评论设置一个权重值,然后再把评论保存到 Sorted Set 中。Sorted Set 的 ZRANGEBYSCORE 命令就可以按权重排序后返回元素。这样的话,即使集合中的元素频繁更新,Sorted Set 也能通过 ZRANGEBYSCORE 命令准确地获取到按序排列的数据。
在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,建议优先考虑使用 Sorted Set。
二值状态统计
二值状态就是指集合元素的取值就只有 0 和 1 两种。
应用场景: 用户签到
解决方案: 选择 Bitmap
Bitmap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。 可以把 Bitmap 看作是一个 bit 数组。
Bitmap 提供了 GETBIT/SETBIT 操作,使用一个偏移值 offset 对 bit 数组的某一个 bit 位进行读和写。不过,需要注意的是,Bitmap 的偏移量是从 0 开始算的,也就是说 offset 的最小值是 0。当使用 SETBIT 对一个 bit 位进行写操作时,这个 bit 位会被设置为 1。Bitmap 还提供了 BITCOUNT 操作,用来统计这个 bit 数组中所有“1”的个数。
统计 ID 3000 的用户在 2020 年 8 月份的签到情况
- 第一步,执行下面的命令,记录该用户 8 月 3 号已签到。
SETBIT uid:sign:3000:202008 2 1
key = uid:sign:3000:202008 ;offset = 2(因为是3号); value = 1
- 第二步,检查该用户 8 月 3 日是否签到。
GETBIT uid:sign:3000:202008 2
- 第三步,统计该用户在 8 月份的签到次数。
BITCOUNT uid:sign:3000:202008
统计出 10 天连续签到的用户总数
Bitmap 支持用 BITOP 命令对多个 Bitmap 按位做“与”“或”“异或”的操作,操作的结果会保存到一个新的 Bitmap 中。
三个 Bitmap bm1、bm2 和 bm3,对应 bit 位做“与”操作,结果保存到了一个新的 Bitmap 中(示例中,这个结果 Bitmap 的 key 被设为“resmap”)。
在统计 1 亿个用户连续 10 天的签到情况时,把每天的日期作为 key,每个 key 对应一个 1 亿位的 Bitmap,每一个 bit 对应一个用户当天的签到情况。接下来,对 10 个 Bitmap 做“与”操作,得到的结果也是一个 Bitmap。在这个 Bitmap 中,只有 10 天都签到的用户对应的 bit 位上的值才会是 1。最后,用 BITCOUNT 统计 Bitmap 中的 1 的个数,即为连续签到 10 天的用户总数。
基数统计
基数统计就是指统计一个集合中不重复的元素个数。
应用场景: 网页 UV 统计
解决方案: Redis 的集合类型中,Set 类型默认支持去重。
有一个用户 user1 访问 page1 时,把这个信息加到 Set 中。
SADD page1:uv user1
当需要统计 UV 时,直接用 SCARD 命令返回一个集合中的元素个数。
如果 page1 非常火爆,UV 达到了千万,这个时候,一个 Set 就要记录千万个用户 ID。对于一个搞大促的电商网站而言,这样的页面可能有成千上万个,如果每个页面都用这样的一个 Set,就会消耗很大的内存空间。
也可以用 Hash 类型记录 UV。
把用户 ID 作为 Hash 集合的 key,当用户访问页面时,就用 HSET 命令(用于设置 Hash 集合元素的值),对这个用户 ID 记录一个值“1”,表示一个独立访客,用户 1 访问 page1 后,就记录为 1 个独立访客,如下所示:
HSET page1:uv user1 1
即使用户 1 多次访问页面,重复执行这个 HSET 命令,也只会把 user1 的值设置为 1,仍然只记为 1 个独立访客。当要统计 UV 时,可以用 HLEN 命令统计 Hash 集合中的所有元素个数。
Hash 类型和 Set 类型相似,当页面很多时,会消耗很大的内存空间。
有什么办法既能完成统计,还能节省内存?
HyperLogLog 是一种用于统计基数的数据集合类型,它的最大优势就在于,当集合元素数量非常多时,它计算基数所需的空间总是固定的,而且还很小。
在 Redis 中,每个 HyperLogLog 只需要花费 12 KB 内存,就可以计算接近 2^64 个元素的基数。
在统计 UV 时,用 PFADD 命令(用于向 HyperLogLog 中添加新元素) 把访问页面的每个用户都添加到 HyperLogLog 中。
PFADD page1:uv user1 user2 user3 user4 user5
用 PFCOUNT 命令直接获得 page1 的 UV 值
PFCOUNT page1:uv
HyperLogLog 的统计规则是基于概率完成的,所以它给出的统计结果有一定误差,标准误算率是 0.81%。 这也就意味着,使用 HyperLogLog 统计的 UV 是 100 万,但实际的 UV 可能是 101 万。虽然误差率不算大,但是,如果需要精确统计结果的话,最好还是继续用 Set 或 Hash 类型。
Redis 集合类型比较
这篇关于12 | 有一亿个keys要统计,应该用哪种集合?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!