Redis bitmap位图操作(图解)

https://blog.csdn.net/youandme520/article/details/122921951

https://www.csdn.net/tags/MtTaIg0sOTY1NDUxLWJsb2cO0O0O.html

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

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

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

位图适用于一些特定的应用场景,比如用户签到次数、或者登录次数等。上图是表示一位用户 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. >>> bin(ord("h"))

  2. '0b1101000'

  3. >>> bin(ord("e"))

  4. '0b1100101'

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


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

 
  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"

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

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

 
  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 开始。示例如下:

 
  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"

2) GETBIT命令

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

 
  1. 127.0.0.1:6379> GETBIT user:1 0

  2. (integer) 1

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

 
  1. redis> EXISTS bits

  2. (integer) 0

  3. redis> GETBIT bits 100000

  4. (integer) 0

3) BITCOUNT命令

统计指定位区间上,值为 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
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值