使用Java Jedis(v2.9.0)获取Redis Bitmap置位偏移值遇异常
解决Redis Bitmap与Java BitSet的转换异常问题
我之前也踩过这个Redis Bitmap和Java BitSet转换的坑,你的问题核心在于两者的存储字节序和位顺序完全不匹配,只靠反转字节数组的方式根本不够,才会出现异常行为。
问题根源
Redis的SETBIT命令和Java BitSet的位映射规则完全相反:
- Redis侧:偏移量
n对应的存储位置是:- 字节索引为
n / 8(第一个字节对应偏移0-7) - 字节内的位索引是
7 - (n % 8)——也就是说,每个字节的最高位(bit7)对应该字节的第一个偏移位,最低位(bit0)对应最后一个偏移位
- 字节索引为
- Java BitSet侧:默认的
fromByteArray方法是把每个字节的bit0对应偏移byteIndex*8,bit7对应偏移byteIndex*8+7,字节顺序也是按偏移递增直接映射。
只反转字节数组(你用的fromByteArrayReverse)只能解决字节顺序的问题,但每个字节内部的位顺序还是错的,这就导致你获取的偏移量和设置的不一致,甚至触发异常。
正确的转换实现
你需要实现一个同时处理字节顺序反转和字节内部位反转的转换方法:
public static BitSet redisBitmapToBitSet(byte[] redisBytes) { // 先反转字节数组,适配Redis的大端存储逻辑 byte[] reversedBytes = new byte[redisBytes.length]; for (int i = 0; i < redisBytes.length; i++) { reversedBytes[i] = redisBytes[redisBytes.length - 1 - i]; } BitSet bitSet = new BitSet(); int currentBit = 0; for (byte b : reversedBytes) { // 反转单个字节内的位,匹配Java BitSet的位顺序 for (int bitInByte = 7; bitInByte >= 0; bitInByte--) { if ((b & (1 << bitInByte)) != 0) { bitSet.set(currentBit); } currentBit++; } } // 清理BitSet中多余的空位(Redis会自动填充高位空字节) int highestSetBit = -1; for (int i = bitSet.nextSetBit(0); i >= 0; i = bitSet.nextSetBit(i + 1)) { highestSetBit = i; } return highestSetBit != -1 ? bitSet.get(0, highestSetBit + 1) : bitSet; }
修改你的业务代码
把原来的fromByteArrayReverse替换成上面的方法即可:
jedis.setbit("foo", 335, true); jedis.setbit("foo", 1000, true); jedis.setbit("foo", 1006, true); byte[] redisBytes = jedis.get("foo").getBytes(); BitSet bitset = redisBitmapToBitSet(redisBytes); for (int i = bitset.nextSetBit(0); i >= 0; i = bitset.nextSetBit(i + 1)) { // 这里会正确输出335、1000、1006,执行你的处理逻辑 }
额外注意事项
- 你的Jedis版本2.9.0是二进制安全的,
jedis.get("foo").getBytes()不会有编码问题,可以放心使用 - 当Redis Bitmap的偏移量很大时,会自动填充前面的空字节,转换后的BitSet通过
nextSetBit遍历只会获取到你设置过的有效位,不会影响业务逻辑
内容的提问来源于stack exchange,提问作者user1189332
相关产品推荐
相关产品推荐

