你知道Redis中的getbit、setbit的作用吗?

Redis常用的有五种数据结构,String、Hash、List、set、zset。 而getbit和setbit则是String中特殊的获取数据方式。 String底层数据是用二进制来存储的,而我们获取到的值就是通过二进制转换来的,而getBit就是直接获得某一位对应二进制的值。

1.什么是偏移量

在计算机里所有的数据都是以二进制的形式存储的,每一个非中文字符占一个字节(Byte),中文字符占两个字节,而一个字节又是占8bit。

先在redis中设置一个k-v(key:foo value:bar)

在这里插入图片描述

在Redis中的存储形式转换成二进制就是(一个字节8bit):01100010 01100001 01110010,而偏移量实际上指的就是从左往右数,偏移量是几就是第几位,偏移量0就是第0位。

在这里插入图片描述

注意:

  1. setbit 只能是0或者1,如果是其他值会报错。
  2. setbit最大的偏移量为4294967295,超过就会报错。

2.常用的方法

这里主要演示一下在代码中使用spring-redis api来操作。

  1. getbit/setbit 获取对应的二进制值
// key是对应的key
// offset偏移量
// true则是值1 false则为0
redisTemplate.opsForValue().setBit(key, offset, true);
  1. bitcount 统计一共有多少位是1
// springredis没有提供的api方法
// 需要用到底层提供的execute方法使用connection去统计
long count = (long) redisTemplate.execute((RedisCallback<Object>) conn -> conn.bitCount(signInRedisKey.getBytes()));
  1. bitField 统计偏移量范围内的值
/** key就是值
   offset是偏移多少,比如10
   from就是从多少开始数 比如 0
   那么就会返回 0-10 一百个数转换成的十进制数
   比如 0000000010 就返回 2 
   注意:返回的是List,集合中只有第一个数有值,如果该key不存在那么就会返回0
**/
List<Long> list = redisTemplate.execute((RedisCallback<List<Long>>) conn ->
                conn.bitField(key.getBytes(), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(offset).valueAt(from)));

3.用处

相信很多人刚了解的时候都会有这个疑问,我使用场景只是存储字符串,也不需要去拿其某一位二进制数据啊。

不知道大家有没有思考过,钉钉的群聊 10人未查看,5人已读是怎么实现的。

如果单纯一条信息存储一个已读数和未读数,这样会造成大量数据库读写,一个群聊几十上百人,信息一天可能达到几万条,如此高频读写,数据库怎么吃得消?何况要怎么记录是哪个人读了,哪个人没读?

我们用setbit看一看能不能实现,比如我们当前是 群 1 第 1001 条消息 一共有100个人 每个人对应一个id假设 0-99 setbit group:message:1001:1 99 1 使用bitcount就可以统计一共有多少人已读。

现在有新的问题,如果有用户退群了呢?比如用户2退群了

那么记录一下退群数 +1 总人数 +1,如果已读人数=总人数-退群数,那么就说明已读了,否则是未读数。

那么新的进群用户我们就可以 setbit group:message:1001:1 100 1,省略了传统集合扩容操作,但切记最大值是4294967295。

总结

redis的getbit、setbit特性可以帮助解决很多现实问题,而且因为存储空间小,一位就一个bit,所以能够很好的节省空间、提高性能。在了解该特性之前,没想到它能解决那么多场景问题,如打卡、群聊等,而仔细了解后发现,如此解决真的很巧妙。

end
  • 作者:Endwas(联系作者)
  • 发表时间:2022-03-01 14:23
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:如果是转博主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者名字和博客地址
  • 评论

    2233
    liuyan