最近在做小程序后端时,用 redis 解决了几个典型问题:缓存 access_token、记录用户订阅状态、分布式锁防止并发。借此机会,我总结了 redis 在业务中最常用的几种模式,希望对大家有帮助。相关代码,可以参考 wx小程序实战
一、为什么选择 redis?
首先 redis 是一款基于内存的键值存储系统,速度快、数据结构丰富、支持持久化。在需要高性能缓存、分布式锁、计数器、消息队列等场景下,redis 几乎是首选。
我将以 spring boot + redistemplate 为例,展示几种常见用法。

二、用法一:缓存(string 类型 + 过期时间)
场景
微信小程序的 access_token 有效期为 2 小时(7200 秒),且每日获取次数有限(2000 次)。必须缓存起来,避免频繁调用。
实现
// 获取 token
string token = redistemplate.opsforvalue().get("wechat_access_token");
if (token == null) {
token = fetchfromwechat();
redistemplate.opsforvalue().set("wechat_access_token", token, 7000, timeunit.seconds);
}关键点
- 设置 过期时间(略小于微信的有效期,提前刷新)
- 配合 分布式锁 防止缓存击穿(见用法四)
三、用法二:存储业务状态(string 类型 + 过期时间)
场景
用户订阅消息后,需要在后端记录订阅关系,30 天后自动失效(与微信授权有效期对齐)。
实现
string key = "subscribe:" + templateid + ":" + openid; redistemplate.opsforvalue().set(key, "1", 30, timeunit.days); // 检查是否订阅 boolean exists = redistemplate.haskey(key);
扩展
可以用 hash 存储一个用户订阅的多个模板:hset user:openid templateid 1
但简单场景下 string 更直观
四、用法三:计数器(自增/自减)
场景
限制用户每天发送意见的次数(防刷)。例如每个村民每天最多提交 3 条意见。
实现
string key = "opinion:limit:" + openid + ":" + localdate.now();
long count = redistemplate.opsforvalue().increment(key);
if (count == 1) {
// 设置过期时间为当天结束
redistemplate.expire(key, 1, timeunit.days);
}
if (count > 3) {
return result.error("今日提交次数已达上限");
}其他应用
- 统计点赞数、阅读数
- 接口限流(滑动窗口)
五、用法四:分布式锁(string + setifabsent + lua)
场景
多实例部署时,缓存失效瞬间多个线程同时请求微信 access_token,导致 token 被覆盖或超限。需要分布式锁保证只有一个线程去刷新。
实现
string lockkey = "wechat:access_token_lock";
string lockvalue = uuid.randomuuid().tostring();
boolean locked = redistemplate.opsforvalue()
.setifabsent(lockkey, lockvalue, 5, timeunit.seconds);
if (locked) {
try {
// 双重检查
string token = redistemplate.opsforvalue().get("wechat_access_token");
if (token == null) {
token = fetchfromwechat();
redistemplate.opsforvalue().set("wechat_access_token", token, 7000, timeunit.seconds);
}
return token;
} finally {
// 释放锁:lua 脚本保证原子性,只释放自己加的锁
string script = "if redis.call('get', keys[1]) == argv[1] then return redis.call('del', keys[1]) else return 0 end";
redistemplate.execute(new defaultredisscript<>(script, long.class),
collections.singletonlist(lockkey), lockvalue);
}
} else {
// 睡 100ms 后重试
thread.sleep(100);
return getaccesstoken();
}注意
- 锁必须有过期时间,防止死锁
- 释放锁时必须验证 value,防止误删他人的锁
- 使用 lua 脚本保证 get + del 的原子性
六、用法五:消息队列(list / pubsub)
场景
异步处理耗时任务,例如发送订阅消息后需要记录日志,但不影响主流程响应。
用 list 实现简单队列
// 生产者:入队
redistemplate.opsforlist().rightpush("msg:queue", message);
// 消费者:阻塞弹出(配合 while 循环或定时任务)
string message = redistemplate.opsforlist().leftpop("msg:queue", 10, timeunit.seconds);用 pub/sub 实现广播
// 发布
redistemplate.convertandsend("channel:opinion", "新意见");
// 订阅(需实现 messagelistener)
@bean
public messagelisteneradapter listeneradapter(redisreceiver receiver) {
return new messagelisteneradapter(receiver, "onmessage");
}对比
| 方式 | 特点 | 适用场景 |
|---|---|---|
| list | 点对点,支持阻塞等待 | 简单任务队列 |
| pub/sub | 广播,消息即发即失(不持久化) | 实时通知、聊天 |
| stream(5.0+) | 持久化、消费组 | 可靠消息队列 |
七、用法六:set / sortedset 实现标签和排行榜
set 应用:用户标签系统
// 给用户打标签
redistemplate.opsforset().add("tag:grid_memeber", openid1, openid2);
// 判断是否网格员
boolean isgrid = redistemplate.opsforset().ismember("tag:grid_memeber", openid);sortedset 应用:排行榜
// 增加积分
redistemplate.opsforzset().incrementscore("rank:opinion", openid, 1);
// 获取前三名
set<string> top3 = redistemplate.opsforzset().reverserange("rank:opinion", 0, 2);八、总结
| 数据结构 | 典型用法 | 你的项目中的应用 |
|---|---|---|
| string | 缓存、计数器、分布式锁 | access_token、订阅状态、限流 |
| hash | 存储对象字段 | (可扩展)用户配置 |
| list | 消息队列 | 异步任务 |
| set | 标签、去重 | 用户角色标识 |
| sortedset | 排行榜、延迟队列 | 积分排行 |
| pub/sub | 实时广播 | 通知推送 |
ok,结束。
到此这篇关于redis 在业务中的几种典型用法的文章就介绍到这了,更多相关redis 业务中用法内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论