聚合统计
所谓的聚合统计,就是指统计多个集合元素的聚合结果,包括:统计多个集合的共有元素 (交集统计);把两个集合相比,统计其中一个集合独有的元素(差集统计);统计多个 集合的所有元素(并集统计)。
场景题:统计手机 App 每天的新增用户数和第二天的留存用户数
- 在Redis中维护一个累积用户的集合,可以就用set数据类型,该集合的value保存的是累积登录的用户id。
- 然后,还需要把每一天登录的用户 ID,记录到一个新集合中,我们把这个集合叫作每 日用户 Set。
- 统计每天新增用户时:只用计算每日用户 Set 和累计用户 Set 的差集就行。
- 统计第二天留存用户时,只需要将今天的用户set和昨天的用户set取差集就是了。
Set 的差集、并集和交集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计 算,会导致 Redis 实例阻塞。所以,一个小建议:可以从主从集群中选择一个从库,让它专门负责聚合计算,或者是把数据读取到客户端,在客户端来完成聚合统计,这样就可以规避阻塞主库实例和其他从库实例的风险了。
排序统计
场景题:在电商网站上提供最新评论列表
最新评论列表包含了所有评论中的最新留言,这就要求集合类型能对元素保序,也就是说,集合中的元素可以按序排列,这种对元素保序的集合类型叫作有序集合。
List 是按照元素进入 List 的顺序进行排序的,而 Sorted Set 可以根据元素的权重来排 序,我们可以自己来决定每个元素的权重值。比如说,我们可以根据元素插入 Sorted Set 的时间确定权重值,先插入的元素权重小,后插入的元素权重大。
在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,建议优先考虑使用 Sorted Set。
这种场景下,选择评论时间的时间戳作为Sorted Set的Score,这样越新的评论就会排在前面。
二值状态统计
现在,我们再来分析下第三个场景:二值状态统计。这里的二值状态就是指集合元素的取 值就只有 0 和 1 两种。在签到打卡的场景中,我们只用记录签到(1)或未签到(0),所 以它就是非常典型的二值状态,在签到统计时,每个用户一天的签到用 1 个 bit 位就能表示,一个月(假设是 31 天)的签 到情况用 31 个 bit 位就可以,而一年的签到也只需要用 365 个 bit 位,根本不用太复杂 的集合类型。这个时候,我们就可以选择 Bitmap。这是 Redis 提供的扩展数据类型。
题目:要统计 ID 3000 的用户在 2020 年 8 月份的签到情况
- 执行命令:SETBIT uid:sign:3000:202008 2 1 ,表示记录8月3号已签到
- 查询8月3日的签到情况,执行命令: GETBIT uid:sign:3000:202008 2
- 查询8月份签到情况,执行命令:BITCOUNT uid:sign:3000:202008
题目:统计1亿个用户连续10天的签到的总数
Bitmap 支持用 BITOP 命令对多个 Bitmap 按位做“与”“或”“异或”的操作,操作的结果会保存到一个新的 Bitmap 中。
在统计 1 亿个用户连续 10 天的签到情况时,你可以把每天的日期作为 key,每个 key 对应一个 1 亿位的 Bitmap,每一个 bit 对应一个用户当天的签到情况。
接下来,我们对 10 个 Bitmap 做“与”操作,得到的结果也是一个 Bitmap。在这个 Bitmap 中,只有 10 天都签到的用户对应的 bit 位上的值才会是 1。
最后,我们可以用BITCOUNT 统计下 Bitmap 中的 1 的个数,这就是连续签到 10 天的用户总数了。
现在,我们可以计算一下记录了 10 天签到情况后的内存开销。每天使用 1 个 1 亿位的 Bitmap,大约占 12MB 的内存(10^8/8/1024/1024),10 天的 Bitmap 的内存开销约 为 120MB,内存压力不算太大。不过,在实际应用时,最好对 Bitmap 设置过期时间,让 Redis 自动删除不再需要的签到记录,以节省内存开销。