redis 使用 get 命令读取 bitmap 类型的数据

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 在签到统计场景中,可以使用 bitmap 数据类型高效的存储签到数据,但 getbit 命令只能获取某一位值,就无法最优的满足部分业务场景了。

在签到统计场景中,可以使用 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天

注释较多,业务代码不多,多多理解~

来源:https://segmentfault.com/a/1190000017470443

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
8天前
|
NoSQL Redis
Redis的数据淘汰策略有哪些 ?
Redis 提供了 8 种数据淘汰策略,分为淘汰易失数据和淘汰全库数据两大类。易失数据淘汰策略包括:volatile-lru、volatile-lfu、volatile-ttl 和 volatile-random;全库数据淘汰策略包括:allkeys-lru、allkeys-lfu 和 allkeys-random。此外,还有 no-eviction 策略,禁止驱逐数据,当内存不足时新写入操作会报错。
42 16
|
8天前
|
缓存 NoSQL 关系型数据库
Redis和Mysql如何保证数据⼀致?
在项目中,为了解决Redis与Mysql的数据一致性问题,我们采用了多种策略:对于低一致性要求的数据,不做特别处理;时效性数据通过设置缓存过期时间来减少不一致风险;高一致性但时效性要求不高的数据,利用MQ异步同步确保最终一致性;而对一致性和时效性都有高要求的数据,则采用分布式事务(如Seata TCC模式)来保障。
42 14
|
8天前
|
存储 NoSQL 算法
Redis分片集群中数据是怎么存储和读取的 ?
Redis集群采用哈希槽分区算法,共有16384个哈希槽,每个槽分配到不同的Redis节点上。数据操作时,通过CRC16算法对key计算并取模,确定其所属的槽和对应的节点,从而实现高效的数据存取。
35 13
|
8天前
|
存储 NoSQL Redis
Redis的数据过期策略有哪些 ?
Redis 采用两种过期键删除策略:惰性删除和定期删除。惰性删除在读取键时检查是否过期并删除,对 CPU 友好但可能积压大量过期键。定期删除则定时抽样检查并删除过期键,对内存更友好。默认每秒扫描 10 次,每次检查 20 个键,若超过 25% 过期则继续检查,单次最大执行时间 25ms。两者结合使用以平衡性能和资源占用。
30 11
|
9天前
|
监控 NoSQL 测试技术
【赵渝强老师】Redis的AOF数据持久化
Redis 是内存数据库,提供数据持久化功能,支持 RDB 和 AOF 两种方式。AOF 以日志形式记录每个写操作,支持定期重写以压缩文件。默认情况下,AOF 功能关闭,需在 `redis.conf` 中启用。通过 `info` 命令可监控 AOF 状态。AOF 重写功能可有效控制文件大小,避免性能下降。
|
9天前
|
存储 监控 NoSQL
【赵渝强老师】Redis的RDB数据持久化
Redis 是内存数据库,提供数据持久化功能以防止服务器进程退出导致数据丢失。Redis 支持 RDB 和 AOF 两种持久化方式,其中 RDB 是默认的持久化方式。RDB 通过在指定时间间隔内将内存中的数据快照写入磁盘,确保数据的安全性和恢复能力。RDB 持久化机制包括创建子进程、将数据写入临时文件并替换旧文件等步骤。优点包括适合大规模数据恢复和低数据完整性要求的场景,但也有数据完整性和一致性较低及备份时占用内存的缺点。
|
12天前
|
存储 NoSQL PHP
如何用Redis高效实现点赞功能?用Set?还是Bitmap?
在众多软件应用中,点赞功能几乎成为标配。本文从实际需求出发,探讨如何利用 Redis 的 `Set` 和 `Bitmap` 数据结构设计高效点赞系统,分析其优缺点,并提供 PHP 实现示例。通过对比两种方案,帮助开发者选择最适合的存储方式。
25 3
|
12天前
|
存储 NoSQL Java
Redis命令:列表模糊删除详解
通过本文的介绍,我们详细探讨了如何在Redis中实现列表的模糊删除。虽然Redis没有直接提供模糊删除命令,但可以通过组合使用 `LRANGE`和 `LREM`命令,并在客户端代码中进行模糊匹配,来实现这一功能。希望本文能帮助你在实际应用中更有效地操作Redis列表。
25 0
|
1月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
74 6
|
8天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题