Redis 的bitmap byte转为bit
bitmap
我们知道redis的bitmap本身不是一种数据结构,底层实际上依靠字符串进行存储,可以借助字符串进行位操作,由于redis的字符串最大内存位512MB,所以bitmap的bit位也是有上限的,8 * 1024 * 1024 * 512 = 2^32,由于C语言字符串的末尾都有一位分隔符,所以bitmap存储的实际上限位 2^32 - 1,所以我们可以借助bitmap来实现很多功能,最常用的比如签到功能,比如用一年一个key,所占用存储空间位 365 / 1024 = 0.36kb,假设有10w个用户全部参与签到 0.36 * 100000 / 1024 = 35MB,10w个用户1年才占用35Mb存储空间,就可以签到功能,想比如结构化数据库,所占存储空间可以说是非常小,并且签到数据保留的作用性不是很大,也没有这个必要,所以设置这些key的过期时间为1年
应用
以签到为例,如果需要获取一年当中的某一天是否签到,只需要getbit,对应的offset为dayOfYear,如果需要统计一年当中已经签到了多少天,可以直接使用bitcount进行统计,但是如果想统计已经签到的天数,具体到一年的第几天就需要进一步操作了
由于存储的时候是设置的bit值,所以不能直接用get方法获取值,需要获取当前字符串的byte
如果用jedis更方便获取byte值,这里使用的是stringRedisTemplate,稍微🤏复杂一点
String redisKey = "";
byte[] bytes = redisTemplate.execute((RedisConnection connection) -> {
Jedis jedis = (Jedis) connection.getNativeConnection();
return jedis.get(redisKey.getBytes());
});
得到的结果为byte[],下一步需要将bytes[]转位二进制,也就是还原为redis底层存储的bit位数组结构,一个byte为8个bit,需要先讲byte转为数字,再转为二进制
对数字和字节进行转换。
假设数据存储是以大端模式存储的:
- byte: 字节类型 占8位二进制 00000000
- char: 字符类型 占2个字节 16位二进制 byte[0] byte[1]
- int : 整数类型 占4个字节 32位二进制 byte[0] byte[1] byte[2] byte[3]
- long: 长整数类型 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4] byte[5]
- long: 长整数类型 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7]
- float: 浮点数(小数) 占4个字节 32位二进制 byte[0] byte[1] byte[2] byte[3]
- double: 双精度浮点数(小数) 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4]byte[5] byte[6] byte[7]
/**
* byte转无符号int
*
* @param byteValue byte值
* @return 无符号int值
*/
public static int byteToUnsignedInt(byte byteValue) {
return byteValue & 0xFF;
}
如Integer.toBinaryString(13) 为1101;因为要还原成redis实际存储的值,所以这里要进行补0,这里也有好几种方法,可以使用StringUtils.leftPad(s, 8, "0")进行补0 ,也可以
Integer.toBinaryString((tByte & 0xFF) + 0x100).substring(1)得到8位二进制数,0x100为100000000=256,,再从第1位进行截取就得到了二进制值,所以可以进行下面这么写
private static final List<String> byteToStr(byte[] bytes) {
List<String> result = new ArrayList<>();
StringBuilder stringBuilder = new StringBuilder();
for (byte aByte : bytes) {
stringBuilder.append(Integer.toBinaryString((aByte & 0xFF) + 0x100).substring(1));
}
String str = stringBuilder.toString();
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '1') {
result.add(String.valueOf(i));
}
}
return result;
}
byte[] result = {0, 13, -64, -31, 101, 88, 47, -64};
System.out.println(byteToStr(result));
最后得到的结果为:
[12, 13, 15, 16, 17, 24, 25, 26, 31, 33, 34, 37, 39, 41, 43, 44, 50, 52, 53, 54, 55, 56, 57]
这样就计算出了一年当中有哪些天进行了签到
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
2019-05-24 python 爬取豆瓣电影短评并wordcloud生成词云图