详解redis的bitmap

48 阅读4分钟

背景

假设有这么一个需求:需要记录、并且统计公司员工一年的签到状态。如果使用MySQL来存储,则格式大致为

字段名字段类型
idBigint
uidstring
datestring
is_checktinyint

如果公司有10万员工,则一年的存储量为3650万条数据,显然存储占用是非常大的。而且随着表数据量变大,查询也会变慢。那有没有其他更优的存储方式呢?

是有的。Redis中的bitmap内存开销小、效率高且操作简单,很适合用于签到这类场景。下面就一起来学习下bitmap这种数据结构。

什么是bitmap

bitmap,中文名称也叫「位图」。其并不是 Redis 的基本数据类型(比如 String、List) 而是基于 String 数据类型的按位操作,高阶数据类型的一种。由于string最大支持512MB。对应到bitmap刚好为2^32位。

Bitmap 实现原理

Bitmap的基本原理就是使用bit位的0或者1 来表示元素的二义性(比如是否签到,是否有效,是否是黑名单用户)

比如对于8个bit位,我们可以通过将idx = 2 和 idx = 5 置为1来表示是否签到,也就是说:1个Byte就可以表示8个用户的签到状态。

Bitmap语法

SETBIT key offset value

命令描述

  • 针对key存储的字符串值,设置或清除指定偏移量offset上的位(bit)

  • 当key不存在时,会创建一个新的字符串

时间复杂度

  • O(1)

example

// uid =1023在2023-10-18-01进行签到
setbit  2023:1018:01 1023 1

GETBIT key offset

命令描述

  • 返回key对应的字符串中 offset位置的位(bit)

时间复杂度

  • O(N)

使用

// 得到1
getbit 2023:1018:01 1023

BITCOUNT key [start end]

命令描述

  • 统计给定字符串中,比特值为1的数量

  • 默认会统计整个字符串,同时也可以通过指定 startend 来限定范围

  • startend 也可以是负数,-1表示最后一个字节,-2表示倒数第二个字节。注意这里是字节,1字节=8比特

  • 如果key不存在,返回0

  • **返回值:**bit值为1的数量

时间复杂度

  • O(N)

使用

// 得到bit=1的个数
bitcount 2023:1018:01

BITPOS key bit [start [end]]

命令描述

  • 返回字符串中,从左到右,第一个比特值为bit(0或1)的偏移量

  • 默认情况下会检查整个字符串,但是也可以通过指定startend变量来指定字节范围,与BITCOUNT中的范围描述一致

  • **返回值: **第一个比特值为指定bit(0或1)的偏移量

时间复杂度

  • O(N)

Example

// 输出1023
bitpos 2023:1018:01 1

BITOP operation destkey key [key ...]

命令描述

  • 对多个字符串进行位操作,并将结果保存到destkey

  • operation 可以是 AND、OR、XOR 或者 NOT

  • BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN,对多个key求逻辑与,并将结果保存到destkey

  • 除了 NOT 操作之外,其他操作都可以接受一个或多个 key 作为输入。

不同长度的字符串

当给定的参数中,字符串长度不同时,较短的那个字符串与最长字符串之间缺少的部分会被看作 0

空的 key 也被看作是包含 0 的字符串序列。

返回值:将各个key的bit位进行位操作后得到的结果

bitmap应用场景

用户签到

需求: 展示用户的签到状态。

签到:

  • setbit 日期 uid 1

  • setbit uid 日期 1

查询用户某日的签到状态

  • getbit uid 日期 1

查询某日登陆的用户总数

  • bitcount 日期

统计活跃用户(用户登陆情况)

需求:如果用户在近一周内每天都登陆,则认为是活跃用户。

登陆: setbit 日期 uid 1

查询近一周连续登陆的用户:bitop and dest 20240201 20240202 .. 20240207

dest 中值为1的offset,就是连续7天活跃用户的ID,bitcount dest即可统计近一周活跃过的用户总和。

统计用户是否在线

登陆: setbit login uid 1

下线: setbit login uid 0

查询是否在线: getbit login uid

参考资料