首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Redis 明明用了,为什么系统还是慢?常见误区与最佳实践

Redis 明明用了,为什么系统还是慢?常见误区与最佳实践

原创
作者头像
用魔法才能打败魔法
发布2025-11-21 15:02:21
发布2025-11-21 15:02:21
1130
举报

前言

有段时间我对 Redis 的感情很复杂:喜它的速度,恨它的“被误用”。团队里常有人跟我说一句话:“加了 Redis,咋还不如没加呢?”我以前以为是 Redis 啥配置没弄好,后来多看了几次线上事故,才发现大部分问题根本不是 Redis,而是我们自己把缓存用成了拖油瓶。

讲几个我自己亲眼经历、真真实实发生在生产上的例子,都是痛到怀疑人生那种。希望你看完能减少一点线上背锅的概率。

为什么用了 Redis 却感觉更慢?

我遇到过一个特别典型的:业务同学在一个用户详情查询接口上加了 Redis,理论上能把 15ms 的 DB 查询干到 1ms 左右。但上线后接口平均 RT 从 20ms 直接飙到 80ms,大家都懵了,以为 Redis 配置坏了。我接过活看日志才发现:每次请求查 Redis 的 key 根本就不存在,然后业务又去查 MySQL,把结果序列化成 JSON 后再写回 Redis,还顺便设置了个高达 3600 秒的 TTL。这些都不算致命,问题是:每个 key 的值都是一个 200KB 的超大 JSON。写入 Redis 的时候网络直接吞不下。

你要知道 Redis 不是魔法,它是内存数据库,瓶颈往往不在 CPU,而是在网络。你塞一个 200KB 的 JSON,上行网络延迟能直接干到几十毫秒。

BigKey

BigKey 是我心目中排名前三的 Redis 杀手。很多人以为 Redis 很快,就随便往里塞东西。一次我排查一个线上 QPS 2000 的接口一直抖动,发现 Redis 客户端的 timeout 次数特别多。抓包一看,每次 GET 一个 key 都要拉 300KB 数据。你想想,一个 GET 操作返回一个 300KB 的大 JSON,解压、反序列化、传输,RT 肯定不可能稳定在 1ms。

我后来把 key value 拆成几个子 key,每个 key 大概 10KB,RT 从原来的 20ms 稳定到 2ms。BigKey 最大的问题不是内存,而是:

  • 网络传输太慢
  • 单线程阻塞其他请求
  • decode/encode 需要 CPU
  • 迁移、同步、集群 reshard 时巨慢

你以为是 MySQL 慢,其实你 Redis 早在默默崩溃了。

热 Key 打爆单节点

另一个大坑就是热 Key。我们一个活动系统的首页展示数据(全平台都要访问)被放到 Redis 一个固定 key 里,结果促销那天 QPS 从 5k 突破到 20k,Redis CPU 直接干到 95%,整个集群读延迟暴涨,甚至出现了 cluster failover。

热 Key 为什么危险?因为 Redis 单线程处理命令,某个 key 太热就等于你把所有请求都打到单个 CPU 上。

后来我们改成两招:

  1. key 加随机前缀,比如 key_${1...10}:data
  2. 客户端读的时候随机挑一份

瞬间从单点压力变成分摊压力。

锁和原子操作不好好用,会把系统卡到怀疑人生

很多人用 Redis 当分布式锁,不是用 set nx px,就是 lua 脚本也懒得写,释放锁时直接 del。然后就出现“误删别人的锁”“锁提前释放”等问题,甚至线上出现过一条接口被锁 5 秒,整个接口 RT 飙到 5.xxx 秒那种诡异现象。

还有人用 incr 做计数,却把它和大量读写混在一起,结果 Redis pipeline 也不用,直接几十次 incr 循环调用,网络往返就能消耗绝大部分时间。

你以为你缺 CPU,其实你缺的是常识。

批量 vs 多次 round-trip,区别到底在哪?

举个实际的计算:某次我查日志看到某个接口一次要从 Redis 获取 30 个 key,但代码里竟然是这样:

代码语言:java
复制
for (String k : keys) {
redis.get(k);
}

一次网络往返大概 0.5ms,30 次就是 15ms,这还是在延迟很低的同机房环境。如果跨机房,直接翻倍。后来我改成 pipeline,大部分请求一下子打包发出去:

代码语言:java
复制
Pipeline p = jedis.pipelined();
for (String k : keys) {
p.get(k);
}
List<Object> res = p.syncAndReturnAll();

RT 直接从 20ms 掉到 2ms。用 Redis,round-trip 是非常关键的点。你以为是 CPU 累了,其实是客户端在跑马拉松。


缓存穿透、击穿、雪崩

缓存穿透:查不存在的数据,每次都直接把数据库打爆。我见过一个手机号查询接口,因为垃圾爬虫疯狂撞手机号,Redis 那边全是 miss,数据库 QPS 从 100 飙到 20000,扛不住。

解决思路不是背出来的,是线上逼出来的:

  • 布隆过滤器拦一层
  • 空值缓存加短 TTL
  • 数据不存在也要记录缓存

缓存击穿:单 Key TTL 正常到期,突然海量请求打进来,打爆数据库。一个 Key 热度高的时候 TTL 不应该随便设置。

缓存雪崩:大量 key 在同一时间过期,机器直接抖成 PPT。解决方式就是加随机 TTL,别偷懒。

缓存一致性

最常见的方式就是双写:写数据库,还写 Redis。但这个问题不是“写不写”,而是“顺序不能乱”。

我见过有人先写 Redis,再写数据库,结果写数据库失败,缓存里存的是错的。应该这样:

  1. 先写数据库
  2. 写 Redis
  3. 如果 Redis 写失败?至少数据库是对的

延迟删除也是常见方案,业务更新时先删缓存,再更新数据库,延迟一点时间再异步删除一遍,保证 eventual consistency。

序列化方式真会影响性能,不是玄学

有一次我们线上 Redis CPU 异常高,查下来是因为大家用的是 JSON 序列化,大对象 decode 超级慢。后来改成了 Protostuff,value 大概缩小了 40%,decode 时间减少了 50% 左右。

序列化的开销不只是 CPU,还有网络。

  • JSON:占空间大、解析慢
  • Hessian:通用但有点重
  • Protostuff / Kryo:高性能但需要 schema

没必要盲目追求最好,用你能接受的最轻的就对了。

Redis cluster 的槽位迁移

集群扩容的时候,把 slot 从一个节点迁移到另一个节点。很多业务方不知道迁移期间访问 slot 会发生 MOVED、ASK 重定向,客户端要做重试。

我见过一次迁移时,某个客户端不支持 ASK 指令,结果访问新 slot 的时候直接报错,整个服务 RT 飙升,所有人都在骂 Redis,其实是客户端不兼容。当然,最惨一次是 BigKey 迁移,迁移一条数据就卡 100ms,看着都想哭。

如果你用 cluster,那你必须:

  • 保证客户端支持 MOVED / ASK
  • BigKey 在迁移之前就拆掉
  • 迁移时监控 latency

Redis 的监控要看啥?我最关注两个指标

latency 和 hit ratio。

latency 可以用 redis-cli 的 latency doctor,看一下是否因为慢命令、AOF、磁盘、fork 导致抖动。

hit ratio(缓存命中率)可以告诉你是否只是 Redis 存在但没起作用。有次我查线上命中率只有 40%,原因是 key 拼错了五次,有不同版本的 key。

监控 Redis 不只是看 CPU 和内存,它最重要的是看请求行为。

数据结构乱用?那基本可以宣布 Redis 白加了

Redis 五种基础结构你都知道,但你可能不知道它们的性能差异巨大。比如:

  • 你用 List 存十万条数据,LRANGE 每次查都要 50ms
  • 用 Hash 存对象,比直接放 JSON 强得多
  • Set 适合去重,但别拿来存几十万条,它查的是 O(n)

你以为 Redis 很快,是因为你没用坏它。

真正反直觉的案例:Redis 越用越慢?

一次线上问题,对象存的是 hash,业务要查十个字段。那同学为了方便,搞了一个 “取整个 hash 再从 Map 里拿字段”。结果每次要从 Redis 取一个 value 有七十多个 field,value 体积大概 50KB,一个接口三次读取,RT 每次 10ms 往上。后来我说:“你用 HMGET 只取用到的字段不行吗?”改完后 RT 降到 2ms 以下。

Redis 快,是建立在你不作死的前提下的。

结语

很多人觉得 Redis 是万能加速器,但在我经历的几十次线上排查中,大部分“Redis 慢”其实都是:

  • 滥用 JSON
  • 用了 BigKey
  • 热 key 集中
  • round-trip 太多
  • 锁乱用
  • 缓存设计混乱
  • 集群迁移踩坑
  • 监控不到位

Redis 没问题,是我们自己把问题引出来的。

如果你也觉得 Redis“没那么快”,我建议从:数据结构、网络延迟、key 大小、客户端配置、缓存策略五个角度逐个排查。往往问题不是出在你想的地方。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 为什么用了 Redis 却感觉更慢?
  • BigKey
  • 热 Key 打爆单节点
  • 锁和原子操作不好好用,会把系统卡到怀疑人生
  • 批量 vs 多次 round-trip,区别到底在哪?
  • 缓存穿透、击穿、雪崩
  • 缓存一致性
  • 序列化方式真会影响性能,不是玄学
  • Redis cluster 的槽位迁移
  • Redis 的监控要看啥?我最关注两个指标
  • 数据结构乱用?那基本可以宣布 Redis 白加了
  • 真正反直觉的案例:Redis 越用越慢?
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档