Redis实现未读消息计数的示例代码

张开发
2026/4/17 7:11:43 15 分钟阅读

分享文章

Redis实现未读消息计数的示例代码
在合伙人系统的产品分配流程中存在两种分配模式核心差异在于是否需要审核直接分配一级城市合伙人向二级销售合伙人分配产品无需审核直接生效间接分配二级销售合伙人向三级流量合伙人分配产品必须经过对应一级城市合伙人审核审核通过后分配才生效。为提升城市合伙人的操作效率小程序需在 “分配产品页面” 为城市合伙人显示待审核数实时提醒其待处理的间接分配申请而未读计数的存储与管理是实现该功能的核心。二、未读计数的实现方案1. 核心触发逻辑在 “间接分配审核接口” 的最后通过一行代码触发未读计数更新直接调用工具类完成城市合伙人未读数量的累加12// 添加未读数默认新增1条待审核提醒appletRedisUtil.addUnreadCount(cityPartner.getId());2. 核心工具类AppletRedisUtil工具类基于 Redis 实现未读计数的 “新增、查询、重置” 全流程管理代码与逻辑解析如下1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253importjakarta.annotation.Resource;importorg.springblade.business.constant.RedisKeyConstant;importorg.springblade.business.pojo.entity.ProductApplyRecord;importorg.springblade.business.service.ProductApplyRecordService;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.stereotype.Component;importjava.util.Objects;Component// 注入Spring容器全局可用publicclassAppletRedisUtil {ResourceprivateRedisTemplateString, Long redisTemplate;// 操作Redis的核心组件// 1. 重载方法默认给城市合伙人新增1条未读消息publicvoidaddUnreadCount(Long miniUserId) {addUnreadCount(miniUserId, 1L);}// 2. 核心方法支持自定义新增未读条数含数据准确性校验publicvoidaddUnreadCount(Long miniUserId, Long value) {// 2.1 工具类无法直接注入Service通过Spring上下文获取ProductApplyRecordService productApplyRecordService SpringContextUtil.getBean(ProductApplyRecordService.class);// 2.2 查该城市合伙人的总申请数未读上限避免未读数超过实际总数Long totalCount productApplyRecordService.lambdaQuery().eq(ProductApplyRecord::getCityPartnerId, miniUserId).count();// 2.3 查当前Redis中的未读数量空值兜底返回0避免空指针Long currentUnread getUnreadCount(miniUserId);// 2.4 修正未读数量若累加后超总申请数取总申请数防止数据异常value (currentUnread value) totalCount ? totalCount : (currentUnread value);// 2.5 更新Redis用“常量前缀用户ID”作为key原子自增未读数量redisTemplate.opsForValue().increment(RedisKeyConstant.PRODUCT_APPLY_UNREAD_NUM miniUserId, value);}// 3. 查询未读数量空值兜底确保返回非nullpublicLong getUnreadCount(Long miniUserId) {String key RedisKeyConstant.PRODUCT_APPLY_UNREAD_NUM miniUserId;Long unread redisTemplate.opsForValue().get(key);returnObjects.isNull(unread) ? 0L : unread;}// 4. 重置未读数量先置0再删key确保状态彻底清空publicvoidresetUnreadCount(Long miniUserId) {String key RedisKeyConstant.PRODUCT_APPLY_UNREAD_NUM miniUserId;redisTemplate.opsForValue().set(key, 0L);redisTemplate.delete(key);}}工具类核心特点数据准确性通过 “总申请数校验” 避免未读数溢出空值兜底避免空指针操作规范性统一 Redis key 格式常量前缀 用户 ID避免 key 混乱功能完整性覆盖 “新增、查询、重置” 三大核心场景支持默认 / 自定义新增条数依赖合理性通过 Spring 上下文获取 Service解决工具类无法直接注入的问题。三、选型为什么用 Redis 而非 MySQL未读计数的核心诉求是“快、并发安全、简单”Redis 完美匹配这些需求而 MySQL 更擅长 “复杂查询、事务一致性、永久存储”具体优势对比如下1. 性能碾压高频场景响应速度差 3 个量级特性Redis内存数据库MySQL磁盘数据库响应时间微秒级1μs 10⁻⁶秒毫秒级1ms 10⁻³ 秒每秒读写能力数万数十万次千级次高频场景表现无卡顿轻松扛住并发如同时 100 个申请提交易出现 “查询卡顿”“写入排队”拖慢数据库2. 操作更轻量避免 MySQL 复杂开销Redis用 increment 原子命令1 行代码完成 “未读数 1”无需锁 / 事务MySQL需执行 UPDATE xxx SET unread_count unread_count 1 WHERE ...还需处理事务隔离级别、行锁竞争代码繁琐且开销大。3. 天然并发安全解决 MySQL 更新冲突当多个请求同时修改同一城市合伙人的未读计数时如同时 2 条申请提交RedisINCR 是单线程原子操作即使 100 个请求同时 1结果也绝对正确0→100MySQL易出现 “并发更新丢失”如两个请求同时读 5都 1 后写 6实际应是 7需额外加 “乐观锁 / 悲观锁”增加复杂度。4. 缓存特性适配减少数据库压力Redis未读计数是 “缓存”所有读写走 RedisMySQL 仅存原始申请记录极大降低 MySQL 访问压力MySQL若直接存储未读数高频读写会占用数据库资源影响核心业务如申请记录查询。5. 灵活扩展支持更多交互场景Redis 的特性可轻松满足未来扩展需求MySQL 难以实现过期自动清理给未读计数 key 设 expire实现 “30 天未读自动失效”无需定时任务批量操作用 MSET/MGET 批量更新 / 查询多个城市合伙人的未读数效率极高丰富计数器操作支持 INCRBY自定义加量、DECR减量、GETSET获取并重置覆盖全场景。四、注意事项Redis 数据安全兜底方案虽然 Redis 是内存数据库但工具类已做足数据安全保障避免数据丢失数据源兜底未读计数的 “源头” 是 MySQL申请记录存在 MySQLRedis 仅为缓存异常修正若 Redis 数据丢失getUnreadCount 会返回 0而 addUnreadCount 会重新查询 MySQL 总申请数自动修正未读计数不会失真。

更多文章