线上 Redis 突然“爆”了,怎么办?

凌晨三点,手机疯狂报警------Redis CPU 100%,内存快满了,业务接口一个个超时......

别慌,这篇文章不堆术语,我会像聊天一样,带你一步步排查问题、稳住现场、顺便把优化思路讲清楚。


一、先搞清楚"爆"是什么意思?

"机器爆了"通常指以下几种情况,不同情况处理方式不一样:

现象(你能看到什么) 最可能的原因
CPU 使用率 100% 请求太多、执行了慢命令(比如 KEYS *)、或者 Redis 在做持久化
内存快用完了 存了太多数据、很多 key 没设过期时间、或者内存碎片太多
连接数爆满 客户端程序没释放连接、连接池设置不合理
网络跑满 传输了大 key(比如一个 value 几 MB),或者一次取太多数据
响应变慢、卡顿 有慢查询、或者 Redis 在做 fork 操作(比如生成快照)

小解释

  • fork:Redis 保存数据到硬盘时,会复制自己一份(子进程),这个过程会短暂卡一下。
  • 大 key:指单个 key 对应的 value 非常大,比如一个 hash 里存了 10 万条数据。
  • 慢查询:执行时间超过预设阈值(比如 10 毫秒)的命令。

二、第一步:紧急"把脉"------查看当前状态

登录到出问题的机器,执行下面几条命令(不用记,收藏就行):

bash 复制代码
# 看 Redis 进程占用了多少 CPU 和内存
top -p $(pgrep redis-server)

# 连上 Redis 看关键指标
redis-cli INFO stats      # 每秒请求数、拒绝连接数等
redis-cli INFO memory     # 用了多少内存、碎片率
redis-cli INFO clients    # 当前有多少客户端连接
redis-cli SLOWLOG GET 10  # 抓出最近的慢命令

如果发现 SLOWLOG 里有一堆 KEYS *,那恭喜你,找到元凶了。下面我们专门讲它。


三、场景一:CPU 飙高 ------ 重点讲 KEYSSCAN

3.1 KEYS 命令:方便但危险

它有什么用?

KEYS pattern 可以查出所有匹配某个模式的 key。

比如你想找所有以 user: 开头的 key:

bash 复制代码
redis-cli KEYS "user:*"

它有什么问题?

它会一次性扫描整个数据库 ,把所有 key 都查一遍。如果库里有 1000 万个 key,这个命令可能要执行好几秒,期间 Redis 无法处理其他请求,CPU 瞬间爆满。生产环境禁止使用!

比喻:就像在一个巨大仓库里,不用索引,挨个箱子翻找所有带"用户"标签的箱子,而且翻完之前不能做任何其他事。

3.2 代替方案:SCAN 命令------温和的游标扫描

SCAN 像一条"可暂停的流水线":每次只取一小部分(比如 100 个),你需要不断重复调用,直到取完所有数据。虽然麻烦一点,但不会阻塞 Redis。

基本用法

bash 复制代码
# 第一次扫描:从游标 0 开始
redis-cli SCAN 0 MATCH "user:*" COUNT 100
# 返回:1) "下一轮的游标值"  2) ["user:1", "user:2", ...]

如果返回的游标是 0,说明扫描结束;否则用这个游标继续:

bash 复制代码
redis-cli SCAN <上一轮返回的游标> MATCH "user:*" COUNT 100

注意COUNT 只是建议每批返回的数量,不保证精确。通常设为 100~1000 即可。

代码示例(Python)

python 复制代码
cursor = 0
while True:
    cursor, keys = redis.scan(cursor, match="user:*", count=100)
    for key in keys:
        # 处理每个 key
        pass
    if cursor == 0:
        break

这样优化后 :原本一个 KEYS 造成的 5 秒阻塞,被拆成几十次毫秒级的扫描,CPU 平稳,用户几乎无感知。


3.3 其他 CPU 杀手及解决

问题命令 替代方案
HGETALL 大 hash HSCAN 分批取
SMEMBERS 大 set SSCAN 分批取
ZRANGE 取全量 加上 LIMIT 分页
SORT 操作 尽量在客户端或数据库做排序

另外如果整体 QPS(每秒请求数)太高,比如超过 5 万,考虑:

  • 加从库做读写分离(读请求走从库)
  • 使用 Redis Cluster 把数据分到多台机器

QPS:Queries Per Second,每秒查询次数。


四、场景二:内存快满了 ------ 先看谁吃掉了内存

4.1 快速找出"大 key"

Redis 自带了扫描大 key 的工具:

bash 复制代码
redis-cli --bigkeys

它会告诉你哪种类型的 key 占内存最多,比如:

  • 最大的 10 个 string
  • 元素最多的 hash / set / list

解决办法

  • 给 key 加上过期时间:EXPIRE keyname 3600(1 小时后自动删除)
  • 拆分大 key:比如把一个有 10 万字段的 hash,拆成 100 个小 hash,按 id 分段
  • 压缩 value:存 JSON 之前先用 gzip 压缩(会略微增加 CPU,但省内存)

4.2 内存碎片是什么?怎么清理?

解释 :你删掉了 10 个 key,但 Redis 并没有把内存立刻还给操作系统,留下很多"小空洞",导致实际占用的内存(used_memory_rss)远大于你真正使用的内存(used_memory)。

查看碎片率

bash 复制代码
redis-cli INFO memory | grep mem_fragmentation_ratio

如果大于 1.5,说明碎片比较严重。

处理办法

  • 重启 Redis(最简单粗暴,但会短暂停服)
  • 如果用的是 Redis 4.0 以上,可以执行 MEMORY PURGE 尝试整理
  • 调整内存分配器(通常用 jemalloc 会好一些)

4.3 设置内存上限和淘汰策略

一定要给 Redis 设置最大内存,否则它会一直吃直到机器死机。

bash 复制代码
# 最大用 8GB
CONFIG SET maxmemory 8gb
# 内存满了怎么办?按 LRU(最近最少使用)算法淘汰旧数据
CONFIG SET maxmemory-policy allkeys-lru

LRU :Least Recently Used,淘汰那些很久没被访问的 key。

其他策略:volatile-lru(只淘汰有过期时间的 key)、noeviction(满了就拒绝写入)等。


五、场景三:连接数爆了 ------ 客户端没"分手"

现象redis-cli INFO clients 显示 connected_clients 接近一万,并且出现 ERR max number of clients reached

原因:你的应用程序每次请求都新建连接,用完却不关闭;或者连接池配置了"永不超时"。

优化

  • 在 Redis 配置中设置空闲连接超时:timeout 300(300 秒无活动就断开)
  • 检查代码:使用连接池(如 JedisPool、Lettuce),并设置 maxTotalmaxIdle
  • 如果瞬间涌入大量客户端(比如抢购),可以在前面加一层代理(如 Twemproxy)来聚合连接

六、场景四:网络带宽打满 ------ 传了太多"大包裹"

现象:网卡流量跑满,Redis 吞吐量下降。

常见原因:有人一次性读了一个几 MB 的 key,并且频繁操作。

排查 :可以用 redis-cli --bigkeys 找到最大的那些 key,看是不是业务在反复读写。

优化

  • 避免一个 value 超过 1 MB,如果必须大 value,考虑压缩
  • 使用 MGET 时,一次不要超过 100 个 key
  • 启用 RESP3 协议(Redis 6 以上)可减少网络往返

七、事后要做的"长期优化"

救火之后,得治本。

7.1 架构级改造

  • 分片:用 Redis Cluster 把数据分散到多个节点,每个节点只存一部分。
  • 读写分离:主库写,从库读,分担压力。
  • 多级缓存:热点数据放到本地内存(比如 Caffeine),访问 Redis 的频率自然就降下来了。

7.2 监控告警(这次不能再裸奔了)

至少监控这些指标:

  • CPU 使用率
  • 内存使用率 + 碎片率
  • 慢查询数量
  • 命中率(keyspace_hits / (keyspace_hits+keyspace_misses),低于 0.8 说明缓存效果差)
  • 主从复制延迟

推荐工具组合:Prometheus + Grafana + redis_exporter,配置好告警规则。

7.3 定期巡检(每季度一次)

  • 执行 SLOWLOG GET 100 分析慢查询
  • 检查有没有 KEYS 被误用(通过监控慢日志)
  • 找出长期不用的 key,批量删除

八、如果 Redis 已经"爆"了,应急三招

  1. 临时牺牲数据一致性

    如果内存爆满,紧急执行 CONFIG SET maxmemory-policy allkeys-lru 让 Redis 自己淘汰旧数据,尽快恢复服务。

  2. 流量切换

    如果有备用 Redis 集群,立刻通过 DNS 或负载均衡切一部分流量过去。

  3. 应用层限流

    在代码里加个限流器:每秒最多向 Redis 发 5000 次请求,超过的直接返回缓存空值或降级数据。


最后说几句

Redis 优化没什么玄学,记住三句话:

  • 不给 Redis 添乱 :不用 KEYS、不存大 key、记得设过期时间。
  • 资源要设上限maxmemory 一定要配,淘汰策略要选对。
  • 监控要早:别等到半夜被报警吵醒才想起来。

如果你在实际中遇到过更奇葩的"爆机"案例,欢迎评论区分享,我们一起排雷。


附录:本文提到的专业词汇速查表

词汇 简单解释
KEYS 命令 遍历所有 key 的命令,会阻塞 Redis,生产环境禁用
SCAN 命令 游标式遍历,每次取一小批,不阻塞
慢查询 执行时间太长的命令,会被记录到 SLOWLOG 里
QPS 每秒请求数,衡量 Redis 的压力
内存碎片 内存被删得七零八落,实际占用比数据多
LRU 淘汰最近最少使用的 key
fork Redis 持久化时复制自身进程,可能短暂卡顿
RESP3 Redis 新版协议,更节约网络流量
相关推荐
用户3074596982072 天前
Redis 延时队列详解
redis
烤代码的吐司君2 天前
Redis 数据结构 ZSet, BIT, HyperLogLog,Geo 空间数据
redis·后端
leeyi5 天前
Checkpoint 机制:Agent 怎么在断电后接着跑
redis·aigc·agent
云技纵横6 天前
一个 @Async 让循环依赖暴雷:Spring 代理的暗坑
redis
犯困蛋挞yy6 天前
用Claude快速解决Redis代码报错反复无解的问题
redis
用户31693538118312 天前
Java连接Redis
redis
小小工匠14 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
taocarts_bidfans15 天前
反向海淘跨境缓存架构优化:taocarts Redis分层缓存实战技术
redis·缓存·架构·反向海淘·taocarts
炘爚15 天前
Linux——Redis
数据库·redis·缓存