redis 使用 get 命令读取 bitmap 类型的数据
2023-09-11 14:16:54 时间
在签到统计场景中,可以使用 bitmap 数据类型高效的存储签到数据,但 getbit 命令只能获取某一位值,就无法最优的满足部分业务场景了。
比如我们按年去存储一个用户的签到情况,365 天,只需要 365 / 8 ≈ 46 Byte,1KW 用户量一年也只需要 44 MB 就足够了。
setbit sign:uid:year 0 1 #第1天
setbit sign:uid:year 1 1 #第2天
...
setbit sign:uid:year 364 1 #第365天
但如果我想获取某个用户一年的签到统计,使用 bitget 命令的话...要循环读取 365 次,这是没办法接受的。
如果能一次读取到以字符串
"1000100010100100...001"
的形式表示的位状态数据,就很好做后续的处理了。
bitmap 其实也是一种特殊的字符串数据,使用 get 命令是可以读取出来的,但是以 16 进制的流数据返回的,这里就涉及到网络编程中数据传输的打包/解包的知识,redis 使用 get 命令读取 bitmap 数据时,将二进制数据打包成了 16 进制返回给我们,所以我们要对此数据包以 16 进制解包,然后转为二进制字符串。给出转换方法:
<?php
// 第1天的签到
$redis->setBit('sign:uid:year', 0, 1);
// 第234天的签到
$redis->setBit('sign:uid:year', 233, 1);
// 第365天的签到
$redis->setBit('sign:uid:year', 364, 1);
// 使用 get 命令一次性读取用户的 bitmap 签到数据
$bitmap_str = $redis->get("sign:uid:year");
// 对数据流使用网络字节序(大端)解包拿到16进制数据的字符串形式
$hex_str = unpack("H*", $bitmap_str)[1];
// hex str 的长度
$hex_str_len = strlen($hex_str);
// 为了防止 hex to dec 时发生溢出
// 我们需要切分 hex str,使得每一份 hex str to dec 时都能落在 int 类型的范围内
// 因为 2 位 16 进制表示一个字节,所以用系统 int 类型的字节长度去分组是绝对安全的
$chunk_size = PHP_INT_SIZE;
// 对 hex str 做分组对齐,否则 str 的最后几位可能会被当作低位数据处理
// 比如 fffff 以 4 位拆分 'ffff', 'f' 后 最后一组 'f' 就被低位数据处理了
// 对齐后 fffff000 分组 'ffff', 'f000' 就能保证 'f' 的数据位了
$hex_str = str_pad($hex_str, $hex_str_len + ($chunk_size - ($hex_str_len % $chunk_size)), 0, STR_PAD_RIGHT);
// 防止 hexdec 时溢出 使用 PHP_INT_SIZE 个 16 进制字符一组做拆分
// 因 16 进制 2 位标识一个字节 所以 PHP_INT_SIZE 是绝对不会溢出的
$hex_str_arr = str_split($hex_str, $chunk_size);
// 位数据的二进制字符串
$bitmap_bin_str = '';
array_walk($hex_str_arr, function($hex_str_chunk) use (&$bitmap_bin_str, $chunk_size) {
$bitmap_bin_str .= str_pad(decbin(hexdec($hex_str_chunk)), $chunk_size * 4, 0, STR_PAD_LEFT);
});
// 一次读取redis即可拿到 bitmap O(n)次操作的数据
echo $bitmap_bin_str{0} . PHP_EOL; //第1天
echo $bitmap_bin_str{233} . PHP_EOL;//第234天
echo $bitmap_bin_str{364} . PHP_EOL;//第365天
注释较多,业务代码不多,多多理解~
相关文章
- redis实战笔记(3)-第3章 Redis命令
- redis分享PPT材料
- Redis单线程单进程为什么效率那么高
- Redis 主从复制
- Redis SCAN命令
- 脚本批量执行Redis命令
- Laravel 中应用 Redis Setex 命令实现锁的方法
- Mysql和redis事物对比
- Redis 内存管理 maxmemory设置内存上线
- Redis使用命令
- 利用redis实现分布式锁
- SpringBoot整合Redis实现Shiro分布式Session共享
- Redis——maven、spring、jedis快速搭建redis工程
- redis-(error)-MISCONF。Redis。is。configuredto。save。RDBsnapshots
- 安装redis集群报错
- Redis 常用监控信息命令总结
- Shiro Redis注入失败,shiro导致redis不能注入
- windows下使用redis,Redis入门使用,Redis基础命令
- Windows环境下Redis
- Redis基础:特点(内存运行、原子性、持久化)、5种基本数据类型与3种特殊数据类型、redis命令、发布订阅机制、事务(单条命令原子性事务不是原子性)、数据备份与恢复、安全
- CentOS 6 安装最新的 Redis 2.8 ,安装 TCMalloc
- redis High Availability---Redis Sentinel翻译
- Redis Get 命令 - 获取指定 key 的值。
- Redis Get 命令 - 获取指定 key 的值。
- ABP 关闭Redis缓存