现有1亿个用户10天的签到情况,你能统计出这10天连续签到的用户总数吗?

2022-12-01 16:02:33 浏览数 (1)

在 Web 和移动应用的业务场景中,我们经常需要保存这样一种信息:统计用户在手机 App 上的签到打卡信息。

在签到打卡的场景中,我们只用记录签到(1)或未签到(0),它就是非常典型的二值状态。在签到统计时,每个用户一天的签到用 1 个 bit 位就能表示,一个月(假设是 31 天)的签到情况用 31 个 bit 位就可以,而一年的签到也只需要用 365 个 bit 位。

Bitmap 数据类型介绍

Redis 恰好有这样一种数据结构:Bitmap。Bitmap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。String 类型是会保存为二进制的字节数组,所以,Redis 就把字节数组的每个 bit 位利用起来,用来表示一个元素的二值状态。你可以把 Bitmap 看作是一个 bit 数组。

那么,具体该怎么用 Bitmap 进行签到统计呢?

假设我们要统计 ID 3000 的用户在 2022 年 10 月份的签到情况,就可以按照下面的步骤进行操作。

第一步,执行下面的命令,记录该用户 10 月 1 号已签到(bit 位设置为 1)。

代码语言:javascript复制
SETBIT uid:sign:3000:202210 0 1

第二步,检查该用户 10 月 1 日是否签到。

代码语言:javascript复制
GETBIT uid:sign:3000:202210 0

第三步,统计该用户在 10 月份的签到次数(bit 数组中所有“1”的个数)。

代码语言:javascript复制
BITCOUNT uid:sign:3000:202210

这样,我们就知道该用户在 10 月份的签到情况了。

Bitmap 的 BITOP 命令

Bitmap 支持用 BITOP 命令对多个 Bitmap 按位做“与”“或”“异或”的操作,操作的结果会保存到一个新的 Bitmap 中。

我以按位“与”操作为例来具体解释一下。从下图中,可以看到,三个 Bitmap bm1、bm2 和 bm3,对应 bit 位做“与”操作,结果保存到了一个新的 Bitmap 中(示例中,这个结果 Bitmap 的 key 被设为“resmap”)。

BITOP命令示意图

回到我们的标题:如果记录了 1 亿个用户 10 天的签到情况,你有办法统计出这 10 天连续签到的用户总数吗?

在统计 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 自动删除不再需要的签到记录,以节省内存开销。

所以,如果只需要统计数据的二值状态,例如商品有没有、用户在不在等,就可以使用 Bitmap,因为它只用一个 bit 位就能表示 0 或 1。在记录海量数据时,Bitmap 能够有效地节省内存空间。

本篇摘自极客时间《Redis核心技术与实战》学习笔记。

0 人点赞