相信不少 Java 开发其实都好奇:一个查询语句的 QPS 到底多少才值得我引入 Redis?说实话,这事没那么死板,不是说你一到 1000 QPS,Redis 小哥就该闪亮登场。但从实战角度来说,有些「信号」确实是在提醒你,是时候把 Redis 请出来救场了。
我就按我平时写业务、搞优化的一些经验来聊聊这事,带你一步一步过一遍:什么时候 Redis 是锦上添花,什么时候它是救命稻草,什么时候你压根不该让它上场。
别急着上缓存
我们先说一个我自己遇到过的坑:
前两年我们系统一个接口突然被业务侧盯上了,说延迟飘得厉害,用户老在 loading。排查一圈发现,一个多表 join 查询(左外连接 + group by + 排序),字段还不少,数据量也有点意思,单查一次差不多得 300ms。这时候 QPS 其实并不高,100 来点,但是用户就一个字:卡。
你说我这时候该怎么办?我啥也别说,Redis 先来一发。
但是你会发现,并不是每一个查询慢的问题,都该用 Redis 来兜底。你要是根源是 SQL 写得跟屎一样,比如你 select *
从七八张表里拼数据出来,那你不该怪数据库扛不住,而是该先照照镜子。
那到底啥时候该考虑引入 Redis?
老规矩,我们从几个维度来分析下:
一、QPS 真的很高,MySQL 顶不住了
经验上,如果你用的是传统的机械硬盘(HDD),单条查询超过 1000 QPS,服务器基本就喘不过气了;如果你是 SSD,可能能扛到 2000 QPS 以上。
但是注意,这只是个「经验值」,不是圣经。你得看你这个 SQL 是不是高消耗型的。如果只是简单的单表 select id from xxx where id=xx
这种,MySQL 顶得住很久,完全可以不急着加 Redis。你加了反而浪费资源。
二、查询结果变化不频繁,而且热点集中
Redis 是干嘛的?说白了,就是给你干 热点数据缓存 的。比如你首页推荐内容,每个人点进来都差不多,或者某篇文章点赞数 5 分钟都不一定涨一次,这种典型的 强读取、弱写入 场景,就该用缓存。
举个栗子:
public ArticleStats getArticleStats(String articleId) {
String cacheKey = "article_stats:" + articleId;
String cached = redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return JSON.parseObject(cached, ArticleStats.class);
}
ArticleStats stats = articleMapper.selectStatsById(articleId);
redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(stats), Duration.ofMinutes(10));
return stats;
}
这段代码基本就是 Redis 缓存的标准套路:先查缓存,查不到再查 DB,然后写回缓存。
这种写法适合读多写少的情况,如果你每 2 秒点赞数就变化一次,那你还这么搞,缓存和 DB 同步问题能让你头秃。
三、延迟高,不堪忍受
还有一种情况,你 QPS 没那么高,但是延迟一查就是两三百毫秒起步,比如业务是多表联合、数据量大、SQL 优化空间又小,那你也可以考虑走缓存,至少让用户别老卡着等页面。
就像我刚才那个例子,一个接口查评论 + 点赞 + 用户信息 + 收藏状态,每个都得 join 一次,还分页、还排序,MySQL 虽然撑得住,但体验实在不行。那时候 Redis 上线之后,用户页面几乎秒开。
四、数据库本身压力大了
这个你用 top
或者监控系统一看就知道了,如果你的数据库负载已经冲上 80%、90%,那你不管 QPS 高不高,是时候用缓存帮它分担点压力了。
就像你爸妈退休了,你不能还天天让他们干活吧,该你上 Redis 了。
再配合一些定时任务或延迟双删策略,效果非常棒。
// 删除评论后,延迟删除缓存,确保数据一致性
commentService.delete(commentId);
Thread.sleep(100);
redisTemplate.delete("article_stats:" + articleId);
五、强一致性业务要小心!
你可别看到 Redis 就跟看到救星一样,啥都往里塞,尤其是那种对数据一致性要求特别高的场景,比如:
这些东西如果你还在 Redis 里搞缓存,弄不好分分钟穿仓穿爆。
我之前就听说某公司把账户余额搞进 Redis,结果因为缓存未及时更新,两个用户同时提现,系统居然都判定「余额充足」,你敢信?等 DB 回过神来,已经赔出去了两份钱。
那 Redis 缓存该怎么上才是「优雅」的?
我来总结一下常用的套路,虽然不是死规则,但挺好用的:
1. 缓存粒度要控制好
你别一个接口的数据全部放成一个大对象扔 Redis,那样更新一个字段都得重写整个缓存。能拆就拆,比如文章信息、作者信息、点赞状态分开缓存,局部更新也方便。
2. 设置合理的过期时间
过期时间太长,数据可能不新;太短,又频繁回源 DB。一般 5~30 分钟看业务,比如首页推荐 10 分钟一刷没啥问题,但订单状态就别超过 1 分钟。
3. 防止缓存穿透 / 击穿 / 雪崩
比如查询的 key 不存在,你要给个「空值」放缓存里,别让它每次都打到 DB(这叫穿透)。要是热点 key 被清了然后瞬间大量请求涌入(击穿),你得加锁或使用互斥锁机制防止重复回源。
面试场 vs 真正项目中
如果你是在面试,恭喜你,请直接答:
❝“我们在查询 QPS 达到 500+ 的时候就会评估引入 Redis 缓存策略,通过读写分离、热点数据缓存和延迟更新等方式来减轻 DB 压力,提高响应速度。”
HR 听了流泪,架构师听了点头。
但你真上项目,你得先看业务场景、数据分布、访问模式、MySQL 的现有性能,不是所有查询多了都得 Redis。
有时候,优化 SQL 本身,比你花一堆时间加缓存还要有效。
最后一句话总结:
❝什么时候上 Redis,不是看 QPS 数字到了没,而是看你真的需要没。
你得问清楚自己几个问题:
如果答案是:查询慢、访问频、数据相对稳定,那就上 Redis。
如果你还没搞清楚 SQL 到底写成什么样就上缓存,那就是掩耳盗铃。