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

📅 2026/6/30 6:43:47
线上 Redis 突然“爆”了,怎么办?
一、先搞清楚“爆”是什么意思“机器爆了”通常指以下几种情况不同情况处理方式不一样现象你能看到什么最可能的原因CPU 使用率 100%请求太多、执行了慢命令比如KEYS *、或者 Redis 在做持久化内存快用完了存了太多数据、很多 key 没设过期时间、或者内存碎片太多连接数爆满客户端程序没释放连接、连接池设置不合理网络跑满传输了大 key比如一个 value 几 MB或者一次取太多数据响应变慢、卡顿有慢查询、或者 Redis 在做 fork 操作比如生成快照小解释forkRedis 保存数据到硬盘时会复制自己一份子进程这个过程会短暂卡一下。大 key指单个 key 对应的 value 非常大比如一个 hash 里存了 10 万条数据。慢查询执行时间超过预设阈值比如 10 毫秒的命令。二、第一步紧急“把脉”——查看当前状态登录到出问题的机器执行下面几条命令不用记收藏就行# 看 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 飙高 —— 重点讲KEYS和SCAN3.1KEYS命令方便但危险它有什么用KEYS pattern可以查出所有匹配某个模式的 key。比如你想找所有以user:开头的 keyredis-cli KEYS user:*它有什么问题它会一次性扫描整个数据库把所有 key 都查一遍。如果库里有 1000 万个 key这个命令可能要执行好几秒期间 Redis 无法处理其他请求CPU 瞬间爆满。生产环境禁止使用比喻就像在一个巨大仓库里不用索引挨个箱子翻找所有带“用户”标签的箱子而且翻完之前不能做任何其他事。3.2 代替方案SCAN命令——温和的游标扫描SCAN像一条“可暂停的流水线”每次只取一小部分比如 100 个你需要不断重复调用直到取完所有数据。虽然麻烦一点但不会阻塞 Redis。基本用法# 第一次扫描从游标 0 开始 redis-cli SCAN 0 MATCH user:* COUNT 100 # 返回1) 下一轮的游标值 2) [user:1, user:2, ...]如果返回的游标是0说明扫描结束否则用这个游标继续redis-cli SCAN 上一轮返回的游标 MATCH user:* COUNT 100注意COUNT只是建议每批返回的数量不保证精确。通常设为 100~1000 即可。代码示例Pythoncursor 0 while True: cursor, keys redis.scan(cursor, matchuser:*, count100) 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 把数据分到多台机器QPSQueries Per Second每秒查询次数。四、场景二内存快满了 —— 先看谁吃掉了内存4.1 快速找出“大 key”Redis 自带了扫描大 key 的工具redis-cli --bigkeys它会告诉你哪种类型的 key 占内存最多比如最大的 10 个 string元素最多的 hash / set / list解决办法给 key 加上过期时间EXPIRE keyname 36001 小时后自动删除拆分大 key比如把一个有 10 万字段的 hash拆成 100 个小 hash按 id 分段压缩 value存 JSON 之前先用 gzip 压缩会略微增加 CPU但省内存4.2 内存碎片是什么怎么清理解释你删掉了 10 个 key但 Redis 并没有把内存立刻还给操作系统留下很多“小空洞”导致实际占用的内存used_memory_rss远大于你真正使用的内存used_memory。查看碎片率redis-cli INFO memory | grep mem_fragmentation_ratio如果大于 1.5说明碎片比较严重。处理办法重启 Redis最简单粗暴但会短暂停服如果用的是 Redis 4.0 以上可以执行MEMORY PURGE尝试整理调整内存分配器通常用 jemalloc 会好一些4.3 设置内存上限和淘汰策略一定要给 Redis 设置最大内存否则它会一直吃直到机器死机。# 最大用 8GB CONFIG SET maxmemory 8gb # 内存满了怎么办按 LRU最近最少使用算法淘汰旧数据 CONFIG SET maxmemory-policy allkeys-lruLRULeast Recently Used淘汰那些很久没被访问的 key。其他策略volatile-lru只淘汰有过期时间的 key、noeviction满了就拒绝写入等。五、场景三连接数爆了 —— 客户端没“分手”现象redis-cli INFO clients显示connected_clients接近一万并且出现ERR max number of clients reached。原因你的应用程序每次请求都新建连接用完却不关闭或者连接池配置了“永不超时”。优化在 Redis 配置中设置空闲连接超时timeout 300300 秒无活动就断开检查代码使用连接池如 JedisPool、Lettuce并设置maxTotal和maxIdle如果瞬间涌入大量客户端比如抢购可以在前面加一层代理如 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_hitskeyspace_misses)低于 0.8 说明缓存效果差主从复制延迟推荐工具组合Prometheus Grafana redis_exporter配置好告警规则。7.3 定期巡检每季度一次执行SLOWLOG GET 100分析慢查询检查有没有KEYS被误用通过监控慢日志找出长期不用的 key批量删除八、如果 Redis 已经“爆”了应急三招临时牺牲数据一致性如果内存爆满紧急执行CONFIG SET maxmemory-policy allkeys-lru让 Redis 自己淘汰旧数据尽快恢复服务。流量切换如果有备用 Redis 集群立刻通过 DNS 或负载均衡切一部分流量过去。应用层限流在代码里加个限流器每秒最多向 Redis 发 5000 次请求超过的直接返回缓存空值或降级数据。最后说几句Redis 优化没什么玄学记住三句话不给 Redis 添乱不用KEYS、不存大 key、记得设过期时间。资源要设上限maxmemory一定要配淘汰策略要选对。监控要早别等到半夜被报警吵醒才想起来。如果你在实际中遇到过更奇葩的“爆机”案例欢迎评论区分享我们一起排雷。附录本文提到的专业词汇速查表词汇简单解释KEYS 命令遍历所有 key 的命令会阻塞 Redis生产环境禁用SCAN 命令游标式遍历每次取一小批不阻塞慢查询执行时间太长的命令会被记录到 SLOWLOG 里QPS每秒请求数衡量 Redis 的压力内存碎片内存被删得七零八落实际占用比数据多LRU淘汰最近最少使用的 keyforkRedis 持久化时复制自身进程可能短暂卡顿RESP3Redis 新版协议更节约网络流量