原文:5.【Redis系列】Redis的高级应用-位图
假设一个应用场景:我们需要记录用户一年的签到记录,签到了是1,没签是0,记录365天,当用户上亿后,存储空间是惊人的。
为了解决这个问题,redis提供了位图的数据结构。这样每天的签到记录只占据一个位,365天就是365个位,46个字节完全可以容纳下。
位图不是特殊的数据结构,它的内容就是普通的字符串,也就是byte数组,我们可以用set/get方法来设置和获取位图的内容,也可以使用位图操作getbit和setbit将byte数组看成位数组来处理。
基本使用
Redis 的位数组是自动扩展,如果设置了某个偏移位置超出了现有的内容范围,就会自动将位数组进行零扩充。
接下来我们使用位操作将字符串设置为 hello (不是直接使用 set 指令),首先我们需要得到 hello 的 ASCII 码,用 Python 命令行可以很方便地得到每个字符的 ASCII 码的二进制值。
>>> bin(ord('h')) '0b1101000' # 高位 -> 低位 >>> bin(ord('e')) '0b1100101' >>> bin(ord('l')) '0b1101100' >>> bin(ord('l')) '0b1101100' >>> bin(ord('o')) '0b1101111'
接下来我们使用 redis-cli 设置第一个字符,也就是位数组的前 8 位,我们只需要设置值为 1 的位,如上图所示,h 字符只有 1/2/4 位需要设置,e 字符只有 9/10/13/15 位需要设置。值得注意的是位数组的顺序和字符的位顺序是相反的。
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"
上面这个例子可以理解为「零存整取」,同样我们还也可以「零存零取」,「整存零取」。「零存」就是使用 setbit 对位值进行逐个设置,「整存」就是使用字符串一次性填充所有位数组,覆盖掉旧值。
零存零取
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 127.0.0.1:6379> getbit w 1 # 获取某个具体位置的值 0/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-cli 会显示该字符的 16 进制形式。
127.0.0.1:6379> setbit x 0 1 (integer) 0 127.0.0.1:6379> setbit x 1 1 (integer) 0 127.0.0.1:6379> get x "\xc0"
来源:https://www.cnblogs.com/lonelyxmas/p/12515047.html