之前有一篇文章讲了位图的使用,然后简单做了个测试,模拟了签到的场景。
但是还是存在着一些问题,如果一个人一年一条签到记录,那么问题来了,怎么快速知道某个人在某个时间段内的签到次数呢?或者是想要批量修改某个时间段的签到呢?
bitCount指令只支持到以字节为单位,明显是不能支持以位为单位来查询的操作。
修改签到功能的话,那么setBit指令是支持的,但是批量修改总不能频繁去请求redis吧?
以上俩个问题都可以通过BitFields指令来解决。
那么下面来使用一下BitFields
环境需要
redis 3.2之后的版本
高->低位
h - > 1101000
e - > 1100101
l - > 1101100
l - > 1101100
o - > 1101111
根据位的范围进行查询
127.0.0.1:6379> set test hello
OK
127.0.0.1:6379> BITFIELD test get u8 0
1) (integer) 104
127.0.0.1:6379> BITFIELD test get u64 0
(error) ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.
127.0.0.1:6379> BITFIELD test get u63 0
1) (integer) 3761268982783410176
上面2个指令
第一个指令是直接设置一个key=test value=hello的一个键值对
第二个指令的话就是从0开始截取8位,返回来的值是无符的,刚刚好是h的字符的值,01101000转成十进制就是104,u就是无符,i就是有符,有符最多支持64位,无符则是63位。因为此我们能够截取的数据最多也就到64位了。当超过长度范围时会报错。
批量修改位的数值
首先我们直接设置一个key=test value=hello的一个键值对
其次将0到8位值,设为数字97的无符号二进制值,97刚刚好就是a的二进制值
最后查询一下,hello变成aello,此外更新的时候redis会把之前的值给返回104就是h的值
127.0.0.1:6379> set test hello
OK
127.0.0.1:6379> BITFIELD test set u8 0 97
1) (integer) 104
127.0.0.1:6379> get test
"aello"
另外在使用的时候需要注意几个点,无符或者有符,虽然我们在使用业务场景时不一定有负数,我们设置值的时候设置有符无符并不重要,关键是取值的时候,若是有符,则最高位是符合位。
127.0.0.1:6379> set test hello
OK
127.0.0.1:6379> BITFIELD test set i8 0 -97
1) (integer) 104
127.0.0.1:6379> BITFIELD test get i8 0
1) (integer) -97
127.0.0.1:6379> BITFIELD test get u8 0
1) (integer) 159
此外bitfields还支持多个指令的执行
127.0.0.1:6379> BITFIELD test get i8 0 get i8 8
1) (integer) -97
2) (integer) 101
下面接着位图的代码做调整,主要是新增了最下面的俩个方法
@Component
public class BitsOpUtils {
@Autowired
private RedisTemplate<String, String> jedisTemplate;
public boolean setBits(String key, int index, int value) {
//tag为true就是设置1 否则就是设置0
boolean tag = value == Constants.ONE;
return jedisTemplate.execute((RedisCallback<Boolean>) con -> con.setBit(key.getBytes(), index, tag));
}
public Long countBits(String key) {
return jedisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
}
//一次过设置多个位
public List<Long> setBitFields(String key, int index, int value) {
return jedisTemplate.execute((RedisCallback<List<Long>>) con -> con.bitField(key.getBytes(),
BitFieldSubCommands.create().set(BitFieldType.UINT_8).valueAt(index).to(value)));
}
//获取从某个位到某个位的值
public List<Long> count(String key, int index, int limit) {
return jedisTemplate.execute((RedisCallback<List<Long>>) con -> con.bitField(key.getBytes(),
BitFieldSubCommands.create().get(BitFieldType.unsigned(limit)).valueAt(index)));
}
}
简单进行测试
@Test
public void setMutiBits() {
String key = "hello";
int index = 0;
int value = Integer.valueOf("01100001", 2);
List<Long> result = bitsOpUtils.setBitFields(key, index, value);
System.out.println(result);
}
最后输出104,因为h被替换为a了,替换的时候会返回该片段的值
@Test
public void getBits() {
String key = "hello";
int index = 0;
int limit = 8;
List<Long> result = bitsOpUtils.count(key, index, limit);
System.out.println(result);
}
最后返回[97]
如果我们要模拟批量更新签到,或者是查询某个人某个连续时间段内的签到情况,那么通过上面的代码,其实也可以支持了,要做的只是将业务数据转化成入参。就不写这部分代码了。
网友评论