BitMap

介绍

BitMap,即位图,是一串连续的二进制数组(0和1),可以用过偏移量(offset)定位元素。BitMap通过最小的单位bit来进行0|1 的设置,表示某个元素的值或状态,时间复杂度为O(1)。

由于bit是计算机中的最小单位,使用他进行存储将非常节省空间,特别适合一些数据量大且使用二值统计的场景

内部实现

BitMap本身是用String类型作为底层数据结构实现的一种统计二值状态的数据类型。

String类型是会保存为二进制的字节数组,所以,Redis就把字节数组的每个bit为利用起来,用来表示一个元素的二值状态,你可以把BitMap看做是一个bit数组。

常用命令

bitmap基础操作:

# 设置值,其中value只能是0和1
SETBIT key offset value
# 获取值
GETBIT key offset

# 获取指定范围内值为1的个数
# start 和 end 以字节为单位
BITCOUT key start end

bitmap 运算操作:

# bitmap间的运算
# operations 唯一操作符,枚举值
AND 与运算 &
OR 或运算 |
XOR 异或 ^
NOT 取反 ~
# result 计算的结果,会存储在改key中
# key1 ... keyn 参与运算的key,可以有多个,空格分割,not运算只能一个key
# 当BITOP 处理不同长度的字符串时,较短的那个字符串缺少的部分会被看作0。返回值是保存到destkey的字符串长度(以字节byte为单位),和输入key中最长的字符串长度相等。
BITOP [operations] [result] [key1] [keyn...] 

#返回指定key中第一个出现指定value(0/1)的位置
BITPOS [key] [value]

应用场景

Bitmap 类型非常适合二值状态统计的场景,这里的二值状态就是指集合元素的取值就是只有0和1两种,在记录海量数据时,bitmap能够有效地节省内存空间。

签到数据

在签到打卡的场景中,我们只用记录签到(1)或未签到(0),所以他就是非常典型的二值状态。

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

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

第一步,执行下面的命令,记录该用户6月3号已签到。

SETBIT uid:sign:100:202206 2 1

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

GETBIT uid:sign:100:202206 2

第三步,统计该用户在6月份的签到次数。

BITCOUNT uid:sign:100:202206

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

如何统计这个月首次打卡时间呢?

Redis提供了BITPOS key bitValue [start] [end] 指令,返回数据表示bitmap中第一个值为bitValue 的offset位置

在默认情况下,命令将检测整个位图,用户可以通过可选的start 参数和end 参数指定要检测的范围。所以我们可以通过执行这条命令来获取userID=100在2022年6月份首次打卡日期:

BITPOS uid:sign:100:202206 1

需要注意的是,因为offset从0开始的,所以我们需要将返回的value+1

判断用户登录状态

bitmap提供GETBIT、SETBIT 操作,通过一个偏移值offset对bit数组的offset位置的bit位进行读写操作,需要注意的是offset从0开始。

只需要一个key=login_status 表示存储用户登录状态集合数据,将用户ID作为offset,在线就设置为1,下线设置0。通过GETBIT 判断对应的用户是否在线。5000万用户只需要6M的空间。

假如我们需要判断ID=10086的用户登录情况:

第一步,执行一下指令,表示用户已登录:

SETBIT login_status 10086 1

第二步,检查该用户是否登录,返回值1表示已登录:

GETBIT login_status 10086

第三步,登出,将offset对应的value设置为0:

SETBIT login_status 10086 0

连续签到用户总数

如何统计出这连续7天连续打卡用户总数呢?

我们把每天的日期作为bitmap的可以,userId作为offset,若是打卡则将offset位置的bit设置为1.

key对应的集合的每个bit位的数据则是一个用户在改日期的打卡记录。

一共有7个这样的bitmap,如果我们能对这7个bitmap的对应的bit位做 运算。同样的UserID offset 都是一样的,当一个userID 在7个bitmap对应的offset位置bit = 1就说明该用户7天连续打卡.

结果保存到一个新的Bitmap中,我们在通过BITCOUNT 统计bit=1的个数便得到了连续打卡7天的用户总数了。

Redis提供了BITOP operation destkey key [key ...] 这个指令用于对一个或者多个key的bitmap进行位元操作.

  • operation 可以是and、OR、NOT、XOR 。当BITOP处理不同长度的字符串是,较短的那个字符串所缺少的部分会被看作0 。空的key 也被看做是包含0 的字符串序列。

假设要统计3天连续打卡的用户,则是将三个bitmap进行and操作,并将结果保存到destmap中,接着对destmap执行BITCOUNT统计,如下命令:

# 与操作
BITOP AND destmap bitmap:01 bitmap:02 bitmap:03
# 统计bit位 = 1 的个数
BITCOUNT destmap

即使一天产生一个亿的数据,bitmap占用的内存也不大,大约占12MB的内存(10^8/8/1024/1024),7天的Bitmap的内存开销约为84MB。同时我们最好给bitmap设置过期时间,让Redis删除过期的打卡数据,节省内存

Last modification:May 31, 2024
如果觉得我的文章对你有用,请收藏本站