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位。
注意:
- setbit 只能是0或者1,如果是其他值会报错。
- setbit最大的偏移量为4294967295,超过就会报错。
2.常用的方法
这里主要演示一下在代码中使用spring-redis api来操作。
- getbit/setbit 获取对应的二进制值
// key是对应的key
// offset偏移量
// true则是值1 false则为0
redisTemplate.opsForValue().setBit(key, offset, true);
- bitcount 统计一共有多少位是1
// springredis没有提供的api方法
// 需要用到底层提供的execute方法使用connection去统计
long count = (long) redisTemplate.execute((RedisCallback<Object>) conn -> conn.bitCount(signInRedisKey.getBytes()));
- 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,所以能够很好的节省空间、提高性能。在了解该特性之前,没想到它能解决那么多场景问题,如打卡、群聊等,而仔细了解后发现,如此解决真的很巧妙。
评论