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 endbitmap 运算操作:
# 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删除过期的打卡数据,节省内存