当前位置: 首页
编程语言
TP6.0消息已读功能基于Redis Bitmap未读计数方案

TP6.0消息已读功能基于Redis Bitmap未读计数方案

热心网友 时间:2026-07-08
转载
#### 谈到消息已读功能,最轻量且节省内存的方案就是直接使用 `SETBIT` 与 `GETBIT` 指令。不过前提条件是:你的 `message_id` 必须是连续整数,或者能够稳定地映射为连续整数。TP6.0 框架本身并未内置 Bitmap 操作封装,因此需要借助原生 Redis 命令,再配合合理的 key 设计才能落地实现。 怎么用 TP6.0 实现消息已读?Redis Bitmap 与未读计数方案【IM】 ### 为什么不用 Redis Set 存储未读 ID? 乍看起来,用 Set 存储未读 ID 非常直观,但在群聊场景中,内存压力会迅速暴露:一个200人的群,每天发送100条消息,一天下来就会产生2万次 `SADD` 操作。尽管 `SCARD` 查询未读数很快,但若想判断“第 N 条消息我是否已读”,要么需要遍历整个 Set,要么执行交集运算,效率并不理想。更棘手的是,`SREM` 标记已读时无法原子性地更新计数,稍有不慎就会导致计数不准。 反观 Bitmap,每条消息仅需1 bit。200人只需25字节,10万用户也仅占用12.5 KB——这才是 IM 场景所需的存储密度。 ### TP6.0 中如何编写 Bitmap 已读逻辑? TP6.0 的 `think\facade\Cache` 并未直接暴露位操作,必须降级到原生 Redis 实例。建议在模型层或服务层统一封装: - **key 格式**统一为 `msg:read:{message_id}`,注意:不是每个用户一个 key,而是每条消息一个 key,value 是按用户 ID 映射的 bitmap。 - 需要预先维护 `user_id → offset` 的映射表(例如 MySQL 中的 `group_user_map` 表,包含 `group_id`、`user_id`、`offset` 字段),否则无法确定某个用户在 bitmap 中的位置。 - **标记已读**:`$redis->setBit('msg:read:12345', $offset, 1)` - **查询是否已读**:`$redis->getBit('msg:read:12345', $offset)` 返回 `1` 或 `0` - **统计已读人数**:`$redis->bitCount('msg:read:12345')` 不要绕弯路使用 `Cache::store('redis')->handler()` 套壳——它返回的是 ThinkPHP 的封装对象,未必提供 `setBit` 方法。正确做法是从配置中直接获取原生 Redis 实例:`app('cache')->store('redis')->getRedis()`。 ### Bitmap 方案最容易被忽视的三个陷阱 **第一个陷阱:** `message_id` 不能直接当作 offset 使用。Redis bitmap 的 offset 是从 0 开始的无符号整数,最大支持 2³²−1(约 42 亿),但业务中的 message_id 往往是时间戳加雪花 ID,很容易超限。必须进行映射,例如利用数据库自增 `seq_id`,或通过 Redis 的 `INCR` 生成紧凑序列号。 **第二个陷阱:** bitmap key 的过期问题。Redis 不支持对 bitmap 中某一位单独设置过期时间,整个 key 要么永不过期,要么统一过期。如果消息的有效期是 30 天,就需要编写定时任务清理旧 key,或者换一种思路:在 key 中带上时间戳,例如 `msg:read:202606:{message_seq}`。 **第三个陷阱:** TP6.0 默认的 Redis 驱动是 phpredis 而非 predis。predis 对 `setBit` 的整数 offset 参数处理有问题(会转成字符串),因此必须确认你使用的驱动类型。否则 `setBit` 可能写入失败却静默返回 `false`,排查起来相当隐蔽。
来源:https://www.php.cn/faq/2787379.html

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
列表遍历时动态判断阈值并返回相应文本

列表遍历时动态判断阈值并返回相应文本

遍历数值列表时,先筛选满足阈值的元素,再根据结果输出列表或友好提示。推荐使用列表推导式结合条件判断,注意边界用`>=`,空列表自动为假。也可用`any()`提前终止遍历,提升效率。此法简洁,避免显式循环,特别适合阈值筛选。

时间:2026-07-08 06:50
Maven项目中如何强制使用本地构建的依赖版本

Maven项目中如何强制使用本地构建的依赖版本

多模块开发中强制使用本地依赖的正确做法是使用-SNAPSHOT版本并配合`-U`参数强制更新,或重构为多模块项目统一管理生命周期。避免使用版本范围语法或手动复制JAR,确保构建行为可靠可重复。

时间:2026-07-08 06:50
Go语言net.Conn并发写安全与原子性保障解析

Go语言net.Conn并发写安全与原子性保障解析

Go标准库中net Conn支持并发方法调用,但Write()不保证原子性。多goroutine同时写入时,系统可能拆分数据包,导致内容交错,破坏消息边界。必须使用互斥锁或bufio Writer等显式同步机制确保写操作完整性,不可依赖系统调用本身的原子性。

时间:2026-07-08 06:50
Python在Windows系统中获取指定卷标U盘驱动器字母的方法

Python在Windows系统中获取指定卷标U盘驱动器字母的方法

使用Pythonwmi库通过Win32_Volume接口查询Windows系统中卷标为“TOSHIBA”的U盘盘符。需安装wmi和pywin32,注意大小写区分及多设备过滤。仅适用于Windows。

时间:2026-07-08 06:49
TP6.0消息已读功能基于Redis Bitmap未读计数方案

TP6.0消息已读功能基于Redis Bitmap未读计数方案

TP6 0中使用Redis位图实现消息已读标记,每条消息仅占一个比特,内存效率高。需将消息ID映射为连续偏移量,通过SETBIT和GETBIT操作。需提前维护用户ID到偏移量的映射表,注意键过期与驱动类型(phpredis与predis)问题。此方法内存极省,适合海量消息场景,且需确保偏移量唯一。

时间:2026-07-08 06:49
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜