Redis 位图(bitMap)

位图(bitMap)

应用场景

在平时开发中, 可能有一些bool型数据需要存取,如用户一年的签到记录,签了是1, 没签是0, 要记录365天。如果使用普通的key/value, 每个用户要记录365个, 当用户上亿时候, 需要的存储空间是惊人的。

为了解决这个问题, Redis提供了位图数据结构, 这样每天的签到记录只占据一个位, 365天就是365个位, 46个字节就可以完全容纳下, 大大节约了存储空间。

位图不是特殊的数据结构, 它的内容其实就是普通的字符串, 也就是byte数组。可以使用普通的get/set直接获取和设置整个位图的内容, 也可以使用位图操作getbit/setbit等将byte数组看成[位数组]来处理。

基本使用

127.0.0.1:6379> setbit s 1 1  
(integer) 0 
127.0.0.1:6379> setbit s 2 1  
(integer) 0 
127.0.0.1:6379> setbit s 4 1 
(integer) 0 
127.0.0.1:6379> setbit s 9 1  
(integer) 0 
127.0.0.1:6379> setbit s 10 1  
(integer) 0 
127.0.0.1:6379> setbit s 13 1  
(integer) 0 
127.0.0.1:6379> setbit s 15 1  
(integer) 0 
127.0.0.1:6379> get s  
"he" 

# [零存零取]
127.0.0.1:6379> setbit w 1 1 
(integer) 0 
127.0.0.1:6379> setbit w 2 1 
(integer) 0 
127.0.0.1:6379> setbit w 4 1 
(integer) 0
# 获取某个具体位置的值 0/1 
127.0.0.1:6379> getbit w 1  
(integer) 1 
127.0.0.1:6379> getbit w 2 
(integer) 1 
127.0.0.1:6379> getbit w 4 
(integer) 1 
127.0.0.1:6379> getbit w 5 
(integer) 0 

# 整存零取
127.0.0.1:6379> set w h  # 整存 
(integer) 0 
127.0.0.1:6379> getbit w 1 
(integer) 1 
127.0.0.1:6379> getbit w 2 
(integer) 1 
127.0.0.1:6379> getbit w 4 
(integer) 1
127.0.0.1:6379> getbit w 5 
(integer) 0 

统计和查找

Redis提供了位图统计指令bitcount和位图查找指令bitpos, bitcount用来统计定位范围内1的个数, bitpos用来查找指定范围内出现第一个0或1。

比如我们可以通过bitcount统计用户一共签到了多少天, 通过bitpos指令查找用户从哪一天开始第一次签到。如果指定了范围参数[start, end], 就可以统计在某个时间范围内用户签到了多少天, 用户自某天以后的哪天开始签到的。

127.0.0.1:6379> set w hello 
OK 

127.0.0.1:6379> bitcount w 
(integer) 21 

# 第一个字符中 1 的位数 
127.0.0.1:6379> bitcount w 0 0  
(integer) 3 

# 前两个字符中 1 的位数
127.0.0.1:6379> bitcount w 0 1  
(integer) 7 

 # 第一个 0 位 
127.0.0.1:6379> bitpos w 0 
(integer) 0 

# 第一个 1 位 
127.0.0.1:6379> bitpos w 1  
(integer) 1

# 从第二个字符算起,第一个 1 位 
127.0.0.1:6379> bitpos w 1 1 1  
(integer) 9 

# 从第三个字符算起,第一个 1 位 
127.0.0.1:6379> bitpos w 1 2 2 
(integer) 17 

魔术指令bitfield

之前设置(setbit)和获取(getbit)指定位的值都是单个单位的, 如果要一次操作多个位,就必须使用管道来处理。 不过Redis3.2版本后新增了一个功能强大的指令。bitfield有三个子指令, 分别是get/set/incrby,它们都可以对指定位片段进行读写, 但是最多只能处理64个连续的位, 如果超过64位, 就的使用多个子指令, bitfield可以一次只从多个子指令。

127.0.0.1:6379> set w hello 
OK 

# 从第一个位开始取 4 个位,结果是无符号数 (u)
127.0.0.1:6379> bitfield w get u4 0   
(integer) 6

# 从第三个位开始取 3 个位,结果是无符号数 (u)
127.0.0.1:6379> bitfield w get u3 2 
(integer) 5

# 从第一个位开始取 4 个位,结果是有符号数 (i)
127.0.0.1:6379> bitfield w get i4 0  
1) (integer) 6 

# 从第三个位开始取 3 个位,结果是有符号数 (i) 
127.0.0.1:6379> bitfield w get i3 2  
1) (integer) -3 


# 一次执行多个子指令
127.0.0.1:6379> bitfield w get u4 0 get u3 2 get i4 0 get i3 2 
1) (integer) 6 
2) (integer) 5 
3) (integer) 6 
4) (integer) -3 

# 使用set子指令将第二个字符e改为a, a的ASCII码是97
# 从第 8 个位开始,将接下来的 8 个位用无符号数 97 替换
127.0.0.1:6379> bitfield w set u8 8 97  
1) (integer) 101 
127.0.0.1:6379> get w "hallo" 
 
# incrby指令
127.0.0.1:6379> set w hello 
OK 

# 从第三个位开始,对接下来的 4 位无符号数 +1
127.0.0.1:6379> bitfield w incrby u4 2 1  
1) (integer) 11 
127.0.0.1:6379> bitfield w incrby u4 2 1 
1) (integer) 12 
127.0.0.1:6379> bitfield w incrby u4 2 1 
1) (integer) 13 
127.0.0.1:6379> bitfield w incrby u4 2 1
1) (integer) 14
127.0.0.1:6379> bitfield w incrby u4 2 1
1) (integer) 15

# 溢出折返了 
127.0.0.1:6379> bitfield w incrby u4 2 1  
1) (integer) 0 


#饱和截断 SAT 
127.0.0.1:6379> set w hello
OK 
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 11 
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 12
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 13 
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (intege) 14  
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 15  

# 保持最大值
127.0.0.1:6379> bitfield w overflow sat incrby u4 2 1 1) (integer) 15 

#失败不执行 FAIL 
127.0.0.1:6379> set w hello
OK 
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 11 
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 12 
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 13
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 14
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 1) (integer) 15 

 # 不执行
127.0.0.1:6379> bitfield w overflow fail incrby u4 2 1 
1) (nil) 
posted @ 2020-08-31 09:57  phper-liunian  阅读(667)  评论(0编辑  收藏  举报