前言

Redis中提供了一个非常有用的功能,就是二进制位数组,与他相关的命令有SETBIT、GETBIT、BITCOUNT、BITOP,命令都很简单,但是用好是非常不易的,能想出在什么地方使用位数组,对系统有很大的提升。

下面是对这几个命令的解释。

SETBIT

用于将位数组指定偏移量上的二进制位设置值,偏移量从0开始算,注意是从左往右数,能取的值也只有0、 1,

setbit redis 场景_数组


但是我觉得需要配合GET、SET命令才能明白二进制位数组,下面看一个例子。

首先将键test设置一个值:“abcd”,那如何不通过set命令改变值呢?,就可以通过setbit命令。

setbit redis 场景_setbit redis 场景_02

首先要知道”abcd“的二进制是多少,也就是01100001 01100010 01100011 01100100,然后每8位为一组:

01100001  十进制97,ascii=a

01100010  十进制98,ascii=b

01100011  十进制99,ascii=c

01100100  十进制100,ascii=d

如果想改成"bcde",只需要将每个二进制+1,如将01100001(a)+1为01100010(b),这个操作就可以使用SETBIT来完成。

将a改为b,就需要将偏移6的位置置为1,偏移7的位置置为0。

setbit redis 场景_setbit redis 场景_03


将b改为c,就需要将偏移15的位置置为1。

setbit redis 场景_setbit redis 场景_04


将c改为d,就需要将偏移21的位置置为1,22、23置为0。

setbit redis 场景_字符串_05


最后键偏移最后一位的位置为1,就可以把d转换为e。

setbit redis 场景_java_06

SETBIT命令的执行过程如下:

  1. 首先要计算保存offset偏移量指定的二进制位至少需要多少字节,计算方式为len=(offset÷8)+1。
  2. 判断bitarray键保存的位数组的长度是否小于上一步的结果,如果小于,将SDS的长度扩展为上一步计算出的大小,并将所有新扩展空间的二进制位的值设置为0。
  3. 计算offset偏移量指定的二进制位保存在位数组的那个字节,方式为byte=offset ÷8。
  4. 计算bit= (offset mod 8)+1, bit值记录了offset偏移量指定的进制位是byte字节的第几个二进制位。
  5. 然后会根据byte值和bit值,在bitarray键保存的位数组中定位offset偏移量指定的二进制位,先将指定二进制位值保存在oldvalue变量中,然后将新值value设置为这个二进制位的值。
  6. 向客户端返回oldvalue变量的值。

GETBIT

返回位数组 bitarray 在 offset 偏移量上的二进制位的值,这个没啥好说的。

setbit redis 场景_setbit redis 场景_07

GETBIT的执行过程:

  1. 计算offset偏移量指定的二进制位保存在位数组的哪个字节,byte=offset÷8。
  2. 计算offset偏移量指定的二进制位是byte字节的第几个二进制位,bit=(offset mod 8)+1。
  3. 根据byte值和bit值,就能找出offset偏移量指定的二进制位,然后返回这个位的值。

BITCOUNT

统计数组中,值为1的二进制为的数量,上面的”bcde“的二进制是01100010 01100011 01100100 01100101,很明显数一数1的个数是14个。

setbit redis 场景_java_08

BITCOUNT用到了两个算法、查表和variable precision SWAR算法,下面是variable precision SWAR算法的Java实现,赞叹算法的神奇,可以查看https://stackoverflow.com/questions/22081738/how-does-this-algorithm-to-count-the-number-of-set-bits-in-a-32-bit-integer-work这篇文章。

public class Demo {
    public static void main(String[] args) {
        System.out.println(countBit(1));
        System.out.println(countBit(10));
        System.out.println(countBit(249));
    }
    private static int countBit(int i) {
        i = i - ((i >> 1) & 0x55555555);
        i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
        return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
    }
}

BITOP

bitop的格式为:

BITOP operation destkey key [key …]

用于将一个或多个保存二进制位的字符串 key 进行位AND 、 OR 、 NOT 、 XOR操作,并将结果保存到 destkey 上。

举例and运算,and运算只有两个全为1才为1,如a(97) & b(98)为1100000=96

setbit redis 场景_java_09

1100001 a
1100010 b
1100000 96 字符为“`”

其他的 OR 、 NOT 、 XOR的操作也是一个道理,这里就不写了。