一些 bool 型的数据,比如用户一年的签到记录,签了是1没签是0,要记录365天,如果使用普通 kv,每个用户要记录365个,当用户上亿的时候,需要很大的存储空间。
Redis 的位图可以优化这种结构的存储,位图其实就是普通字符串,也就是 byte 数组。
存取
可以通过 setbit、getbit 存取
setbit [key] [index] [value]
getbit [key] [index] [value]
因为本质就是字符串,也可以直接 get set
get 时如果对应字节不可打印,redis-cli 会显示该字符的16进制显示
统计与查找
bitcount [key] 统计 1 的个数
bitcount [key] [start] [end] start-end 范围内 1 的个数(只能指定字节范围)
比如
bigcount w 0 0 # 第一个字符,也就是前8位 中 1 的个数
bigcount w 0 1 # 前两个字符中 1 的个数
bitpos [key] [0|1] [start] [end] 范围内第一个 0 或 1
bitpos w 1 1 1 # 从第二个字符算起,第一个 1 位
bitpos w 1 2 2 # 从第三个字符算起,第一个 1 位
bitfield
bitfield 有 3 个子指令,分别是 get、set、incrby
最多处理64个连续的位,如果超过,可以使用多个子指令。
bitfield get
bitfield w get u4 0 # 从第一个位开始取4个位,结果是无符号数 (u)
bitfield w get i4 0 # 从第一个位开始取4个位,结果是有符号数 (i)
有符号数指获取的位数组中第一个位是符号位,剩下的才是值,如果第一位是1,那就是负数。
一次执行多个子指令
bitfield w get u4 0 get u3 2 get i4 0 get i3 2
bitfield set
bitfield w set u8 8 97 # 从第9个位开始,将接下来的8个位用无符号数97替换
bitfield incrby
用来对指定范围的位进行自增操作。
自增可能会导致上溢出和下溢出,Redis 默认处理是折返,如果溢出,就将溢出的符号位丢掉。
比如:8位无符号数 255,加 1 后会溢出,全部变 0.
8位有符号数 127,加 1 后会溢出变成-128
127.0.0.1:6379[2]> set w hello
OK
127.0.0.1:6379[2]> bitfield w incrby u4 2 1 # 从第三个位开始,对接下来的4 位无符号位 + 1
1) (integer) 11
127.0.0.1:6379[2]> bitfield w incrby u4 2 1
1) (integer) 12
127.0.0.1:6379[2]> bitfield w incrby u4 2 1
1) (integer) 13
127.0.0.1:6379[2]> bitfield w incrby u4 2 1
1) (integer) 14
127.0.0.1:6379[2]> bitfield w incrby u4 2 1
1) (integer) 15
127.0.0.1:6379[2]> bitfield w incrby u4 2 1 # 溢出折返了
1) (integer) 0
bitfield 溢出策略
bitfield 提供了溢出策略指令 overflow,可以设置溢出行为,默认是 折返(wrap)
保持最大值(sat)
127.0.0.1:6379[2]> bitfield w overflow sat incrby u4 2 1
1) (integer) 14
127.0.0.1:6379[2]> bitfield w overflow sat incrby u4 2 1
1) (integer) 15
127.0.0.1:6379[2]> bitfield w overflow sat incrby u4 2 1 # 保持最大值
1) (integer) 15
失败不执行(fail)
127.0.0.1:6379[2]> bitfield w overflow fail incrby u4 2 1
1) (integer) 14
127.0.0.1:6379[2]> bitfield w overflow fail incrby u4 2 1
1) (integer) 15
127.0.0.1:6379[2]> bitfield w overflow fail incrby u4 2 1 # 溢出,失败返回 nil
1) (nil)