# 5.4 位图数据类型的应用

在Redis里,位图(Bitmap)是由一串二进制数字组成的,它不是一种数据类型,而是基于字符串、能面向字节操作的对象.位图的长度不固定,但是在计算机里8位(bit)能组成一个字节(Byte),所以位图的长度一般是8或者是8的倍数

## 5.4.1 `SETBIT`和`GETBIT`操作

* `SETBIT`
  * 语法:`SETBIT key offset value`.其中:
    * `offset`:偏移量(偏移量从左到右计算)
    * `value`:待设置的值
  * 功能:设置指定键的位图数据
* `GETBIT`
  * 语法:`GETBIT key offset`
  * 功能:读取位图指定位数据.若指定位不存在则返回0

例:

设置位图:

```
127.0.0.1:6379> SETBIT myBitmap 0 1
(integer) 0
127.0.0.1:6379> SETBIT myBitmap 1 1
(integer) 0
127.0.0.1:6379> SETBIT myBitmap 2 0
(integer) 0
127.0.0.1:6379> SETBIT myBitmap 3 0
(integer) 0
127.0.0.1:6379> SETBIT myBitmap 5 1
(integer) 0
```

注意:设置位图数据时只能将值设置为0或1:

```
127.0.0.1:6379> SETBIT myBitmap 7 3
(error) ERR bit is not an integer or out of range
```

读取位图:

```
127.0.0.1:6379> GETBIT myBitmap 1
(integer) 1
127.0.0.1:6379> GETBIT myBitmap 3
(integer) 0
```

读取一个不存在的位:

```
127.0.0.1:6379> GETBIT myBitmap 7
(integer) 0
```

## 5.4.2 用`BITOP`对位图进行运算

* `BITOP`
  * 语法:`BITOP operation destkey key [key ...]`.其中:
    * `operation`:操作符.
      * `AND`:按位与
      * `OR`:按位或
      * `XOR`:按位异或(如果两个比较的位相同,则结果为0;如果两个比较的位不同,则结果为1)
      * `NOT`:按位取反
    * `destkey`:用于保存运算结果的key名
  * 功能:操作位图

例:

创建2个位图:

```
127.0.0.1:6379> SETBIT bit1 0 1
(integer) 0
127.0.0.1:6379> SETBIT bit1 1 1
(integer) 0
127.0.0.1:6379> SETBIT bit1 3 1
(integer) 0
127.0.0.1:6379> SETBIT bit2 2 1
(integer) 0
```

即:

* `bit1`:`1011`
* `bit2`:`0100`

按位与操作:

注:按位与操作的结果应为`0000`

```
127.0.0.1:6379> BITOP AND result bit1 bit2
(integer) 1
```

查看结果:

```
127.0.0.1:6379> GETBIT result 0
(integer) 0
127.0.0.1:6379> GETBIT result 1
(integer) 0
127.0.0.1:6379> GETBIT result 2
(integer) 0
127.0.0.1:6379> GETBIT result 3
(integer) 0
127.0.0.1:6379> get result
"\x00"
```

按位或操作:

注:按位或操作的结果应为`1111`

```
127.0.0.1:6379> BITOP OR result bit1 bit2
(integer) 1
```

查看结果:

```
127.0.0.1:6379> GETBIT result 0
(integer) 1
127.0.0.1:6379> GETBIT result 1
(integer) 1
127.0.0.1:6379> GETBIT result 2
(integer) 1
127.0.0.1:6379> GETBIT result 3
(integer) 1
```

按位取反操作:

注:`bit1`按位取反的结果应为`0100`

```
127.0.0.1:6379> BITOP not result bit1
(integer) 1
```

查看结果:

```
127.0.0.1:6379> GETBIT result 0
(integer) 0
127.0.0.1:6379> GETBIT result 1
(integer) 0
127.0.0.1:6379> GETBIT result 2
(integer) 1
127.0.0.1:6379> GETBIT result 3
(integer) 0
```

按位异或操作:

注:按位异或的结果应为`1111`

```
BITOP xor result bit1 bit2
```

查看结果:

```
127.0.0.1:6379> GETBIT result 0
(integer) 1
127.0.0.1:6379> GETBIT result 1
(integer) 1
127.0.0.1:6379> GETBIT result 2
(integer) 1
127.0.0.1:6379> GETBIT result 3
(integer) 1
```

## 5.4.3 `BITCOUNT`操作

* `BITCOUNT`
  * 语法:`BITCOUNT key [start end]`
  * 功能:统计key在指定范围内的1的出现次数.`0, -1`或不写参数表示统计范围为整个key

注:关于`BITCOUNT`命令中的`start, end`:

`start, end`**表示字符串中的字节偏移,而不是比特偏移**.这一点非常重要.

* `start`:是子范围的起始字节位置(从0开始)
* `end`:是子范围的结束字节位置

例如,对于字符串"foobar":

* 字符"f"在字节偏移0中
* 字符"o"在字节偏移1和2中
* 字符"b"在字节偏移3中
* ...以此类推。

如果你执行命令`BITCOUNT key 0 1`.它会计算"fo"中设置为1的比特位的数量,因为"fo"占据了字节偏移0到1

还有一些其他需要注意的事项:

1. 当start和end都是正数或0时,范围包括start和end两端
2. 当start或end是负数时,它表示从字符串的末尾开始的偏移.例如,-1表示字符串的最后一个字节
3. 如果start大于字符串的长度,或end小于0,结果将是 0
4. 如果end在start之后,则它们会被交换.所以命令`BITCOUNT key 2 0`和`BITCOUNT key 0 2`会产生相同的结果

例:统计用户在线天数

设置用户在线天数

```
127.0.0.1:6379> SETBIT user1 0 1
(integer) 0
127.0.0.1:6379> SETBIT user1 3 1
(integer) 0
127.0.0.1:6379> SETBIT user1 7 1
(integer) 0
```

统计用户在线天数:

```
127.0.0.1:6379> BITCOUNT user1
(integer) 3
127.0.0.1:6379> BITCOUNT user1 0 0
(integer) 3
```
