Redis 位图
Redis 位图简介
位图不是实际的数据类型,而是一组针对字符串类型(被视为位向量)定义的位操作。由于字符串是二进制安全 Blob,其最大长度为 512 MB,因此它们适合设置多达 2^32 个不同的位。
你可以在一个或多个字符串上执行按位操作。位图用例的一些示例包括
- 集合的有效表示形式,其中集合的成员对应于整数 0-N。
- 对象权限,其中每一位表示特定权限,类似于文件系统存储权限的方式。
基本命令
请参阅 位图命令的完整列表。
示例
假设有 1000 名骑行者在乡间赛车,他们的自行车上装有 0-999 的传感器。您希望快速确定给定传感器是否在 1 小时内向跟踪服务器发出 ping 以查看骑行者。
您可以使用其键引用当前时间的位图表示此场景。
- 骑行者 123 在 2024 年 1 月 1 日的 00:00 小时向服务器发出 ping。然后,您可以确认骑行者 123 向服务器发出 ping。您还可以检查骑行者 456 是否在同一小时内向服务器发出 ping。
> SETBIT pings:2024-01-01-00:00 123 1
(integer) 0
> GETBIT pings:2024-01-01-00:00 123
1
> GETBIT pings:2024-01-01-00:00 456
0
package io.redis.examples;
import org.junit.Assert;
import org.junit.Test;
import redis.clients.jedis.UnifiedJedis;
public class BitMapsExample {
public void run() {
UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
boolean res1 = jedis.setbit("pings:2024-01-01-00:00", 123, true);
System.out.println(res1); // >>> false
boolean res2 = jedis.getbit("pings:2024-01-01-00:00", 123);
System.out.println(res2); // >>> true
boolean res3 = jedis.getbit("pings:2024-01-01-00:00", 456);
System.out.println(res3); // >>> false
long res4 = jedis.bitcount("pings:2024-01-01-00:00");
System.out.println(res4); // >>> 1
}
}
import redis
r = redis.Redis(decode_responses=True)
res1 = r.setbit("pings:2024-01-01-00:00", 123, 1)
print(res1) # >>> 0
res2 = r.getbit("pings:2024-01-01-00:00", 123)
print(res2) # >>> 1
res3 = r.getbit("pings:2024-01-01-00:00", 456)
print(res3) # >>> 0
r.setbit("pings:2024-01-01-00:00", 123, 1)
res4 = r.bitcount("pings:2024-01-01-00:00")
print(res4) # >>> 1
位操作
位操作分为两组:常量时间单比特操作(如将比特设置为 1 或 0,或获取其值)和对比特组的操作(例如,计算给定比特范围内的已设置比特数(例如,计数)。
位图最大的优势之一是它们在存储信息时通常可以节省极大的空间。例如,在一个不同的用户由递增用户 ID 表示的系统中,可以使用仅 512 MB 的内存记住 40 亿用户的单比特信息(例如,了解用户是否希望接收时事通讯)。
SETBIT
命令以比特数为第一个参数,以要将比特设置为的值为第二个参数,该值为 1 或 0。如果寻址的比特超出当前字符串长度,该命令将自动扩大字符串。
GETBIT
仅返回指定索引处的比特值。超出范围的比特(寻址超出存储在目标键中的字符串长度的比特)始终被视为零。
有三个命令对比特组进行操作
BITOP
对不同的字符串执行位运算。提供的操作包括 AND、OR、XOR 和 NOT。
BITCOUNT
执行计数,报告设置为 1 的位数。
BITPOS
查找第一个具有指定值 0 或 1 的位。
BITPOS
和 BITCOUNT
都能够操作字符串的字节范围,而不是针对字符串的整个长度运行。我们可以轻松地查看位图中已设置的位数。
> BITCOUNT pings:2024-01-01-00:00
(integer) 1
package io.redis.examples;
import org.junit.Assert;
import org.junit.Test;
import redis.clients.jedis.UnifiedJedis;
public class BitMapsExample {
public void run() {
UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
boolean res1 = jedis.setbit("pings:2024-01-01-00:00", 123, true);
System.out.println(res1); // >>> false
boolean res2 = jedis.getbit("pings:2024-01-01-00:00", 123);
System.out.println(res2); // >>> true
boolean res3 = jedis.getbit("pings:2024-01-01-00:00", 456);
System.out.println(res3); // >>> false
long res4 = jedis.bitcount("pings:2024-01-01-00:00");
System.out.println(res4); // >>> 1
}
}
import redis
r = redis.Redis(decode_responses=True)
res1 = r.setbit("pings:2024-01-01-00:00", 123, 1)
print(res1) # >>> 0
res2 = r.getbit("pings:2024-01-01-00:00", 123)
print(res2) # >>> 1
res3 = r.getbit("pings:2024-01-01-00:00", 456)
print(res3) # >>> 0
r.setbit("pings:2024-01-01-00:00", 123, 1)
res4 = r.bitcount("pings:2024-01-01-00:00")
print(res4) # >>> 1
例如,假设您想知道网站用户每日访问的最长连胜。您从零开始计数天数,即您公开网站的那一天,并在用户每次访问网站时使用 SETBIT
设置一个位。作为位索引,您只需获取当前 Unix 时间,减去初始偏移量,然后除以一天中的秒数(通常为 3600*24)。
通过这种方式,对于每个用户,您都会得到一个包含每天访问信息的小字符串。使用 BITCOUNT
,可以轻松获得给定用户访问网站的天数,而使用一些 BITPOS
调用,或简单地获取和分析客户端位图,可以轻松计算最长连胜。
位图很容易拆分成多个键,例如为了对数据集进行分片,而且通常最好避免使用巨大的键。要将位图拆分成不同的键,而不是将所有位都设置到一个键中,一个简单的策略就是每个键存储 M 位,并使用 bit-number/M
获取键名,使用 bit-number MOD M
寻址键内的第 N 位。
SETBIT
和 GETBIT
是 O(1)。BITOP
是 O(n),其中 n 是比较中最长字符串的长度。
了解更多