在平时开发过程中,经常会有一些 bool 类型数据需要存取。比如记录用户一年内签到的次数,签了是 1,没签是 0。如果使用 key-value 来存储,那么每个用户都要记录 365 次,当用户成百上亿时,需要的存储空间将非常巨大。为了解决这个问题,Redis 提供了位图结构。

位图(bitmap)同样属于 string 数据类型。Redis 中一个字符串类型的值最多能存储 512 MB 的内容,每个字符串由多个字节组成,每个字节又由 8 个 Bit 位组成。位图结构正是使用“位”来实现存储的,它通过将比特位设置为 0 或 1来达到数据存取的目的,这大大增加了 value 存储数量,它存储上限为2^32 

位图本质上就是一个普通的字节串,也就是 bytes 数组。您可以使用getbit/setbit命令来处理这个位数组,位图的结构如下所示:
 

redis怎样缓存图片 redis怎么存图片_位图

位图适用于一些特定的应用场景,比如用户签到次数、或者登录次数等。上图是表示一位用户 10 天内来网站的签到次数,1 代表签到,0 代表未签到,这样可以很轻松地统计出用户的活跃程度。相比于直接使用字符串而言,位图中的每一条记录仅占用一个 bit 位,从而大大降低了内存空间使用率。

Redis 官方也做了一个实验,他们模拟了一个拥有 1 亿 2 千 8 百万用户的系统,然后使用 Redis 的位图来统计“日均用户数量”,最终所用时间的约为 50ms,且仅仅占用  16 MB内存。

位图应用原理

某网站要统计一个用户一年的签到记录,若用 sring 类型存储,则需要 365 个键值对。若使用位图存储,用户签到就存 1,否则存 0。最后会生成 11010101... 这样的存储结果,其中每天的记录只占一位,一年就是 365 位,约为 46 个字节。如果只想统计用户签到的天数,那么统计 1 的个数即可。

位图操作的优势,相比于字符串而言,它不仅效率高,而且还非常的节省空间。

Redis  的位数组是自动扩展的,如果设置了某个偏移位置超出了现有的内容范围,位数组就会自动扩充。

下面设置一个名为 a 的 key,我们对这个 key 进行位图操作,使得 a 的对应的 value 变为“he”。

首先我们分别获取字符“h”和字符“e”的八位二进制码,如下所示:

 接下来,只要对需值为 1 的位进行操作即可。如下图所示:
 

1. >>> bin(ord("h"))
2. '0b1101000'
3. >>> bin(ord("e"))
4. '0b1100101'

redis怎样缓存图片 redis怎么存图片_redis_02

把 h 和 e 的二进制码连接在一起,第一位的下标是 0,依次递增至 15,然后将数字为 1 的位置标记出来,得到 1/2/4/9/10/13/15,我们把这组数字称为位的“偏置数”,最后按照上述偏置数对字符 a 进行如下位图操作。注意,key 的初始二进制位全部为 0。

从上述示例可以得出,位图操作会自动对 key 进行扩容。

如果对应位的字节是不可以被打印的,那么 Redis 会以该字符的十六进制数来表示它,如下所示: 

1. C:\Users\Administrator>redis-cli
2. 127.0.0.1:6379> SETBIT a 1 1
3. (integer) 0
4. 127.0.0.1:6379> SETBIT a 2 1
5. (integer) 0
6. 127.0.0.1:6379> SETBIT a 4 1
7. (integer) 0
8. 127.0.0.1:6379> get hello
9. "h"
10. 127.0.0.1:6379> SETBIT a 9 1
11. (integer) 0
12. 127.0.0.1:6379> SETBIT a 10 1
13. (integer) 0
14. 127.0.0.1:6379> SETBIT a 13 1
15. (integer) 0
16. 127.0.0.1:6379> SETBIT a 15 1
17. (integer) 0
18. 127.0.0.1:6379> get hello
19. "he"

位图常用命令

1. 127.0.0.1:6379> SETBIT b 0 1
2. (integer) 0
3. 127.0.0.1:6379> SETBIT b 1 1
4. (integer) 0
5. 127.0.0.1:6379> get b
6. "\xc0"

1) SETBIT命令

用来设置或者清除某一位上的值,其返回值是原来位上存储的值。key 在初始状态下所有的位都为 0 ,语法格式如下:

SETBIT key offset value

 其中 offset 表示偏移量,从 0 开始。示例如下:

2) GETBIT命令

1. 127.0.0.1:6379> SET user:1 a
2. OK
3. #设置偏移量为0
4. 127.0.0.1:6379> SETBIT user:1 0 1
5. (integer) 0
6. #当对应位的字符是不可打印字符,redis会以16进制形式显示
7. 127.0.0.1:6379> GET user:1
8. "\xe1"

用来获取某一位上的值。示例如下:

 当偏移量 offset 比字符串的长度大,或者当 key 不存在时,返回 0。

1. 127.0.0.1:6379> GETBIT user:1 0
2. (integer) 1

3) BITCOUNT命令

1. redis> EXISTS bits
2. (integer) 0
3. redis> GETBIT bits 100000
4. (integer) 0

统计指定位区间上,值为 1 的个数。语法格式如下:

BITCOUNT key [start end]

示例如下:

1. 127.0.0.1:6379> BITCOUNT user:1
2. (integer) 8

 通过指定的 start 和 end 参数,可以让计数只在特定的字节上进行。start 和 end 参数和 GETRANGE 命令的参数类似,都可以使用负数,比如 -1 表示倒数第一个位, -2 表示倒数第二个位。

在线练习工具:Try Redis 查看更多命令:Command reference – Redis

redis的bitmap能设置最大的长度是多少, 为什么可以设置的最大长度位数是2^32, 怎么计算bitmap会占用多大的空间

前提:  实际上, redis只支持5种数据类型. 并没有bitmap. 也就是bitmap是基于redis的字符串类型的. 而一个字符串类型最多存储512M.

首先: 计算机的单位换算先了解下

         8 bit = 1byte  

         1024 byte = 1kb

         1024 kb = 1Mb

其次:

      我们使用的bitmap指令SETBIT  key  offset value, 这个指令就是将第offset设置成0或1.  比如 SETBIT  ss  1000 1 //就是将1000位置为1.  1 bit就是1位,  所以我们只要将512M换算成bit, 那么就知道bitmap支持的最大设置长度了.  计算如下

      8 * 1024 * 1024 * 512  =  2^32    (所以这个结果就是这么来的) 

怎么计算自己的bitmap会大概占用多大的存储空间呢?  

举个栗子: 今有一个bitmap最大长度1024, 需要占用多大的空间?

解: 长度1024也就是他需要1024个位(bit),  或者单位为byte就是需要 1024 / 8, 即需要128byte