位图不是一种实际的数据类型,而是在字符串类型上定义的一组面向位的操作,字符串类型被视为位向量。由于字符串是二进制安全的大对象(blob),其最大长度为 512 MB,因此它们适合设置多达 2^32 个不同的位。
你可以在一个或多个字符串上执行位操作。位图的一些用例包括:
- 当集合成员对应于 0-N 整数时,高效的集合表示。
- 对象权限,其中每个位代表一个特定权限,类似于文件系统存储权限的方式。
基本命令
SETBIT
将给定偏移量处的位设置为 0 或 1。GETBIT
返回给定偏移量处的位值。
查看位图命令的完整列表。
示例
假设有 1000 名骑行者在乡村竞速,他们的自行车上安装了编号为 0-999 的传感器。你希望快速确定某个传感器是否在一小时内向跟踪服务器发送了心跳,以检查骑行者的状态。
你可以使用一个位图来表示这个场景,位图的键引用当前小时。
- 骑行者 123 在 2024 年 1 月 1 日 00:00 小时内向服务器发送了心跳。然后你可以确认骑行者 123 已向服务器发送心跳。你还可以检查骑行者 456 在同一小时内是否向服务器发送了心跳。
127.0.0.1:6379> SETBIT pings:2024-01-01-00:00 123 1
(integer) 0
127.0.0.1:6379> GETBIT pings:2024-01-01-00:00 123
(integer) 1
127.0.0.1:6379> GETBIT pings:2024-01-01-00:00 456
(integer) 0
位操作
位操作分为两组:常数时间单一位操作,如将位设置为 1 或 0,或获取其值;以及对比特组的操作,例如计算给定位范围中已设置位的数量(例如,总体计数)。
位图的最大优势之一是它们在存储信息时通常能提供极大的空间节省。例如,在一个由递增用户 ID 表示不同用户的系统中,仅使用 512 MB 内存就可以记住 40 亿用户的单一位信息(例如,知道用户是否希望接收新闻通讯)。
SETBIT
命令的第一个参数是位号,第二个参数是要设置的位值(1 或 0)。如果寻址的位超出当前字符串长度,命令会自动扩展字符串。
GETBIT
只返回指定索引处位的值。超出范围的位(寻址的位超出存储在目标键中的字符串长度)始终被认为是零。
有三个对比特组进行操作的命令:
BITOP
在不同字符串之间执行位操作。提供的操作有 AND、OR、XOR 和 NOT。BITCOUNT
执行总体计数,报告设置为 1 的位数。BITPOS
查找第一个具有指定值 0 或 1 的位。
BITPOS
和 BITCOUNT
都能够对字符串的字节范围进行操作,而不是对整个字符串长度进行操作。我们可以轻易地看到位图中已设置的位数。
127.0.0.1:6379> BITCOUNT pings:2024-01-01-00:00
(integer) 1
例如,假设你想知道你的网站用户每日访问的最长连续天数。你从零开始计算天数,也就是你网站公开的那一天,并且每次用户访问网站时都用 SETBIT
设置一个位。作为位索引,你只需获取当前 Unix 时间,减去初始偏移量,然后除以一天中的秒数(通常是 3600*24)。
这样,每个用户都有一小段字符串,其中包含每天的访问信息。使用 BITCOUNT
可以轻松获取给定用户访问网站的天数,而通过几次 BITPOS
调用,或者简单地在客户端获取并分析位图,就可以轻松计算出最长连续天数。
位图很容易拆分为多个键,例如为了数据分片,并且通常最好避免使用巨大的键。要将位图拆分到不同的键中,而不是将所有位设置到一个键中,一个简单的策略是每个键存储 M 个位,并通过 bit-number/M
获取键名,并通过 bit-number MOD M
获取键内要寻址的第 N 个位。
性能
SETBIT
和 GETBIT
的复杂度是 O(1)。 BITOP
的复杂度是 O(n),其中 n 是比较中最长字符串的长度。