李锋镝的博客

  • 首页
  • 时间轴
  • 说说
  • 左邻右舍
  • 博友圈
  • 关于我
    • 关于我
    • 网站地图
    • 网站统计
    • 另一个网站
    • 我的导航站
    • 赞助
  • 留言
  • 走心评论
  • 系列文章
  • Now
  • 每日心情
  • 论坛
  • 🚇开往
Destiny
自是人生长恨水长东
  1. 首页
  2. 中间件
  3. 正文

Redis实例CPU飙高至90%:从根源排查到彻底解决的全攻略

2025年10月16日 约 4,108 字14 分钟 41点热度 0人点赞 0条评论
本文最后更新于 2025年10月16日,距今已 300 天,其中的信息可能已经发生变化,请注意甄别。

在高并发业务场景中,Redis作为高性能缓存中间件,其CPU使用率直接影响业务响应速度。当Redis实例CPU飙高至90%时,若不及时处理,可能导致缓存服务卡顿、业务超时甚至系统雪崩。本文将基于实战经验,从“定位-分析-解决-监控”四个维度,详细拆解Redis CPU高负载的排查与优化方案,帮你快速恢复服务稳定。

一、紧急定位:3步锁定CPU高负载来源

CPU飙高的核心原因无非两类:单条命令执行开销过大或并发请求量超出承载上限。定位阶段需结合Redis内置命令与系统工具,精准锁定根因,避免盲目优化。

1.1 Redis内置命令:聚焦“命令层面”问题

通过redis-cli工具,直接获取命令执行的关键指标,快速找到“罪魁祸首”。

(1)查命令吞吐量:判断是否并发过载

执行命令查看Redis每秒处理的命令总数,评估并发压力:

redis-cli -h [RedisIP] -p [端口] -a [密码] info stats

重点关注以下指标:

  • instantaneous_ops_per_sec:每秒命令执行数,核心判断标准。
    • 正常范围:单实例Redis在普通服务器(4核8G)上,QPS建议控制在10万以内;若超过15万,即使是简单的GET/SET命令,也可能导致CPU满负荷。
    • 异常案例:某电商秒杀活动中,瞬时QPS突破30万,Redis CPU直接飙升至95%,后续通过分片集群将QPS分摊到6个实例,才恢复正常。

(2)查慢命令:锁定高复杂度操作

慢命令是导致CPU飙高的最常见原因,需优先排查。执行命令查看最近的慢命令记录:

# 查看最近20条慢命令(默认记录10ms以上的命令)
redis-cli -h [RedisIP] -p [端口] slowlog get 20

# 临时调小慢命令阈值(如记录5ms以上命令,便于捕捉更多潜在问题)
redis-cli -h [RedisIP] -p [端口] config set slowlog-log-slower-than 5000
高频高CPU慢命令及特征: 命令 复杂度 典型场景 危害
KEYS * O(n) 全库key遍历 数据量10万+时,阻塞主线程数秒,CPU飙升
HGETALL key O(n) 读取哈希表所有字段 哈希表字段数1万+时,单次执行耗时超50ms
SINTER set1 O(n) 多个集合求交集 集合元素数10万+时,计算开销剧增
ZUNIONSTORE O(n) 多个有序集合合并 涉及大集合时,内存与CPU双重消耗

(3)查客户端连接:定位“问题来源”

部分场景下,某台业务服务器可能高频发送高CPU命令,需定位具体客户端:

# 查看所有客户端连接,按cmd列筛选高频命令,按addr列定位来源IP
redis-cli -h [RedisIP] -p [端口] client list

例如,若输出中大量客户端的cmd字段为HGETALL,且addr指向同一台业务服务器,说明该服务器的请求存在优化空间。

1.2 系统工具:确认“Redis是否为唯一 culprit”

需排除其他进程抢占CPU的情况,确保优化方向聚焦Redis。

(1)用top命令:看Redis进程CPU占比

执行top命令后,按P键按CPU占比排序,观察redis-server进程的CPU使用率:

  • 若redis-server占比超过80%,说明Redis是CPU高负载的主因;
  • 若其他进程(如java、nginx)占比高,需先优化对应服务,再回头排查Redis。

(2)用perf工具:细粒度分析Redis内部函数

若需深入定位Redis内部哪个函数消耗CPU,可使用perf工具(需提前安装,如yum install perf):

# 查看Redis进程(PID可通过top或ps aux | grep redis获取)
perf top -p [RedisPID]

关键函数与对应问题:

  • dictFind:哈希表查找函数,占比高说明HGET、HMGET等哈希操作频繁;
  • zslInsert:有序集合插入函数,占比高说明ZADD命令执行过多;
  • scanMatch:SCAN命令的匹配函数,占比高需检查SCAN的COUNT参数是否过大。

1.3 补充检查:避免遗漏“隐性因素”

除上述核心手段外,还需快速确认两个关键点:

  1. 是否开启持久化:若RDB快照或AOF刷盘正在执行,fork进程或磁盘IO可能间接导致CPU升高,可通过info persistence查看持久化状态;
  2. 是否存在主从同步:主从全量同步时,主库生成RDB、从库加载RDB,均会消耗CPU,可通过info replication查看同步状态。

二、深度分析:4类核心原因拆解

定位到初步线索后,需结合业务场景与Redis特性,分析高CPU的根本原因,为后续优化提供依据。

2.1 原因1:高复杂度命令高频执行(最常见)

特征:slowlog中频繁出现O(n)、O(n²)命令,且单次执行耗时超10ms。
典型案例:

  • 某社交平台用KEYS user:*遍历所有用户key(数据量50万),每次执行耗时3秒,CPU直接从20%飙升至90%;
  • 某支付系统用HGETALL order:123读取订单详情(哈希表字段数2000),每秒调用500次,累积CPU开销占比达60%。
    本质:Redis是单线程模型,高复杂度命令会阻塞主线程,导致后续命令排队,CPU长时间处于“计算忙碌”状态。

2.2 原因2:并发QPS超出实例承载上限

特征:instantaneous_ops_per_sec远超10万,且以简单命令(GET、SET)为主。
典型场景:

  • 某电商秒杀活动,瞬时请求量达50万QPS,全部打向单个Redis实例,即使是GET命令,也因“高频执行”导致CPU满负荷;
  • 某缓存穿透场景,因未做布隆过滤器,每秒有20万请求查询不存在的key,Redis需频繁处理“key不存在”的逻辑,累积CPU开销。
    本质:单线程的Redis处理命令时,虽每条简单命令耗时短(微秒级),但高频次下“累积耗时”会耗尽CPU资源。

2.3 原因3:数据结构使用不当

特征:用错数据结构导致命令开销变大,或存储“大对象”增加处理成本。
常见错误用法:

  • 用List实现消息队列,频繁调用LPOP、RPOP(无阻塞,但高频次下效率低于Stream);
  • 用String存储10KB+的JSON大对象(如商品详情),每秒GET/SET 1000次,每次需序列化/反序列化,消耗CPU;
  • 用Sorted Set存储超100万元素的排行榜,频繁调用ZADD和ZRANGE,排序与查询开销大。

2.4 原因4:Redis配置不合理

特征:未开启关键优化项,或配置参数与硬件/业务不匹配。
常见问题配置:

  • IO线程未优化:Redis 6.0+支持多IO线程,但默认io-threads 1(单线程),网络IO瓶颈会间接导致CPU空闲率低;
  • AOF刷盘策略过严:appendfsync always(每写1条命令刷盘1次),高频磁盘IO会占用CPU资源;
  • RDB快照时机不当:save 60 1000(60秒内改1000次就快照),业务高峰时频繁fork进程,导致CPU波动;
  • 未开启惰性删除:lazyfree-lazy-eviction no(过期key实时删除),大量过期key删除时阻塞主线程。

三、落地解决:按“优先级”执行优化方案

优化需遵循“先止血、再根治”的原则,优先解决高频高影响问题,再通过架构与配置优化长期提升稳定性。

3.1 紧急止血:CPU达90%+时的临时方案

若CPU已接近满负荷,需先快速降低负载,避免服务崩溃:

  1. 暂停非核心业务请求:联系业务方,临时下线非关键功能(如运营报表查询、历史数据统计),减少Redis请求量;
  2. 切换从库为主库:若主从架构正常,执行slaveof no one将从库升级为主库,让原主库下线排查;
  3. 重启Redis(谨慎使用):若数据已持久化(RDB/AOF开启),可重启Redis临时释放资源,但需提前告知业务方可能有1-2秒的服务中断;
  4. 临时禁用危险命令:执行config set rename-command KEYS "",禁用KEYS等高频高耗命令,避免进一步恶化。

3.2 核心优化1:替换高复杂度命令(立竿见影)

针对slowlog中的高CPU命令,用低复杂度命令替代,从根源减少CPU开销:

原命令 问题 优化方案 复杂度变化
KEYS * 全量遍历,阻塞主线程 改用SCAN 0 MATCH * COUNT 100(分批遍历) O(n)→O(1)(单次)
HGETALL key 全量取哈希,数据量大 改用HMGET key field1 field2(按需取字段) O(n)→O(k)(k为字段数)
SINTER set1 set2 大集合交集,计算耗时 1. 小集合场景:改用SISMEMBER循环判断;2. 大集合场景:业务层预计算结果存入Redis O(n)→O(1)
ZUNIONSTORE 多有序集合合并,开销大 业务层分批次合并,或用Redis Cluster分摊计算 O(n)→O(n/m)(m为实例数)

补充建议:通过redis.conf永久禁用危险命令,避免后续误调用:

rename-command KEYS ""
rename-command FLUSHDB ""
rename-command FLUSHALL ""

3.3 核心优化2:降低并发压力(扛住高QPS)

当QPS超出单实例承载上限时,需通过“分流”与“防护”降低Redis压力:

(1)扩容:分摊请求量

  • 主从复制:1主多从架构,主库负责写,从库负责读。例如,1主3从可将读QPS分摊到3个从库,主库CPU压力降低60%以上。
    • 注意:从库数量建议不超过5个,过多从库会导致主库同步压力增大;
  • Redis Cluster:分片集群,将数据按槽位(共16384个槽)拆分到多个实例。例如,8个实例的集群,每个实例仅需处理1/8的请求,QPS承载上限提升8倍。
    • 适用场景:QPS超50万、数据量超10GB的场景。

(2)防护:解决缓存穿透/击穿

  • 缓存穿透:请求不存在的key,导致Redis频繁处理无效请求。
    • 方案1:缓存空值,对不存在的key,存储key:null,过期时间设为30秒;
    • 方案2:布隆过滤器,在Redis前部署布隆过滤器,先判断key是否存在,不存在则直接返回,避免请求打向Redis;
  • 缓存击穿:热点key过期时,大量并发请求同时重建缓存,导致Redis CPU飙升。
    • 方案:互斥锁,用SET key value NX EX 3600加锁,确保同一时间只有1个请求能重建缓存,其他请求等待重试。
      # 伪代码逻辑
      if (Redis.get(key) == null):
      if (Redis.set(key_lock, 1, NX, EX 10)):
          # 重建缓存(如从DB查询数据)
          data = DB.query(key)
          Redis.set(key, data, EX 3600)
          Redis.del(key_lock)
      else:
          # 等待100ms后重试
          sleep(100)
          return Redis.get(key)

3.4 核心优化3:优化数据结构与存储方式

选对数据结构、拆分大对象,可显著降低命令执行开销:

(1)拆分大对象

  • 大JSON拆分:将10KB+的JSON对象(如user:123)拆分为哈希表,按字段存储:
    • 原方案:SET user:123 '{"name":"xxx","age":20,"addr":"xxx"}'(单次GET需读取完整JSON);
    • 优化方案:HSET user:123 name "xxx" age 20 addr "xxx"(需哪个字段用HMGET取,减少数据传输与解析开销);
  • 大列表拆分:将10万+元素的列表(如message:123)拆分为多个小列表,按时间或序号分片:
    • 原方案:LPUSH message:123 msg1 msg2 ... msg100000(LRANGE查询时耗时久);
    • 优化方案:LPUSH message:123:202509 msg1 msg2(按日期分片,每次查询仅访问1个小列表)。

(2)选对数据结构

  • 消息队列:用Stream替代List,Stream支持ACK确认、分组消费,避免消息丢失,且性能更稳定;
    • 示例:XADD stream:order * orderId 123 amount 100(生产消息),XREADGROUP GROUP g1 c1 COUNT 10 BLOCK 0 STREAMS stream:order >(消费消息);
  • 计数器:用INCR替代GET+SET,INCR是原子操作,避免并发问题,且执行速度更快;
  • 排行榜:用Sorted Set的ZADD、ZRANGE,避免业务层自己维护排序逻辑,减少CPU消耗。

3.5 核心优化4:调整Redis配置(长期优化)

根据硬件与业务场景,优化Redis配置,减少CPU浪费:

(1)优化IO线程(Redis 6.0+)

# 开启多IO线程,线程数建议为CPU核心数的1/2(如4核CPU设为2,8核设为4)
io-threads 4
# 仅让IO线程处理读命令(写命令仍用主线程,保证原子性)
io-threads-do-reads yes

效果:网络IO耗时降低30%-50%,主线程CPU空闲率提升,可处理更多命令。

(2)优化持久化配置

  • RDB配置:避开业务高峰,减少fork次数:
    # 1小时内修改1000次才触发RDB(默认是60秒10000次,过于频繁)
    save 3600 1000
    # 关闭RDB压缩(压缩会消耗CPU,若磁盘充足可关闭)
    rdbcompression no
  • AOF配置:平衡性能与安全性:
    # AOF刷盘策略:每秒刷1次(避免always的高频IO,也避免no的数据丢失风险)
    appendfsync everysec
    # 开启AOF重写自动触发,减少AOF文件体积,降低IO开销
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb

(3)开启惰性删除与内存优化

# 过期key惰性删除,避免实时删除阻塞主线程
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
# 内存满时的淘汰策略:优先删除过期key,再删除LRU(最近最少使用)key
maxmemory-policy volatile-lru
# 设置最大内存,避免Redis内存溢出,导致系统Swap,间接消耗CPU
maxmemory 16gb

四、长效监控:避免问题复发

优化后需建立监控体系,实时跟踪Redis CPU与命令执行情况,提前发现潜在风险:

4.1 核心监控指标

指标类别 关键指标 正常范围 告警阈值
CPU相关 redis-server进程CPU使用率 <50% >80%(触发告警)
命令相关 instantaneous_ops_per_sec <10万 >15万(触发告警)
慢命令相关 slowlog每秒新增数量 <5条/秒 >20条/秒(触发告警)
内存相关 used_memory_rss / used_memory <1.5(内存碎片率) >2.0(触发告警)
连接相关 connected_clients <1000 >5000(触发告警)

4.2 监控工具选型

  • Prometheus + Grafana:主流监控组合,可通过redis_exporter采集Redis指标,在Grafana中配置仪表盘,实时查看CPU、QPS、慢命令等数据;
  • Redis自带监控:通过info命令定期采集指标,结合脚本实现简单告警(如CPU超80%时发送邮件/短信);
  • 云服务监控:若使用阿里云Redis、腾讯云Redis,可直接开启厂商提供的监控告警功能,无需自建。

总结

Redis CPU飙高并非“绝症”,关键在于“快速定位、精准分析、分级解决”。总结核心流程:

  1. 定位:用slowlog找高耗命令,top/perf确认Redis主因;
  2. 分析:判断是命令复杂度、并发压力、数据结构还是配置问题;
  3. 解决:先紧急止血(停非核心业务、切从库),再优化命令与数据结构,最后扩容与调配置;
  4. 监控:用Prometheus+Grafana长期跟踪,避免问题复发。
除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.lifengdi.com/zhong-jian-jian/4525

推荐阅读

  • Redis 不只是缓存:8 大实战场景 + 深度避坑指南,从入门到架构师级应用
  • Redis 7.0+ 中 EXPIREAT 的增强选项详解
  • Redis的主从同步及Redis Cluster(集群)下的高可用
  • Apollo配置中心中的protalDB的作用是什么
  • 什么是Meta Server?
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: CPU Redis
最后更新:2025年10月16日

岁月同一天 8 月 12 日

回望过去的今天,你在写什么

  • 6 年前 2020年8月12日
    jstat命令使用(JDK1.8)

    概述 jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下: jstat [-命令选项] [vm…

相关文章
  • 缓存架构实战指南:6大核心缓存技术深度解析与落地方案2025年12月23日
  • 从万级到千万级:排行榜系统的6种实现方案深度解析(含原理、优化与实战)2025年10月29日
  • Redis中缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题2020年2月26日
  • Redisson分布式锁的watch dog自动续期机制2023年1月5日
  • 内存屏障浅析2021年11月18日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >
1234567891112131415161718192021222324252627282930313233343536373839404142434446474849505152535455575859606162636465666769727476777879808182858687909293949596979899
取消回复
…

文章评论

还没有评论,快来抢沙发吧~

不将沉重累坠的银元装在怀中,来自讨无谓的苦吃。

听点儿音乐吧 朋友~
文章目录
最新 热点 随机
最新 热点 随机
Kratos+ v1.1.16版本更新说明 WorkBuddy介绍 Kratos+ v1.1.14版本更新说明 Spring Boot 指定外部配置文件的方式 Spring Boot 配置加载优先级总结 Claude Fable 5(claude-fable-5)深度详解
给主题增加了Now、每日心情、年度回顾、岁月同一天、随机漫步等功能AI时代,个人技术博客的出路在哪里?增加了两套复古皮肤-牛皮纸、千禧网页这个域名注册整整十年了,十年时间,真快啊Kratos+ v1.1.14版本更新说明WordPress实现用户评论等级排行榜插件
使用shell脚本统一修改maven项目的版本 图数据库选型:Neo4j、Janus、HugeGraph 今晚,回家过年! ElasticSearch入门-基本概念介绍以及安装 SpringBoot 实现 RSA+AES 自动接口解密 Kafka常见面试题(一)
最近评论
李锋镝 发布于 2 天前(08月10日) 等我搞一个数据转换的插件~
李锋镝 发布于 2 天前(08月10日) 精美可担不起~😂
李锋镝 发布于 2 天前(08月10日) PHP是世界上最伟大的语言=。=
Hary 发布于 3 天前(08月09日) 我都想用了,但是是ty,转换有点麻烦
老张博客 发布于 4 天前(08月08日) 做的越来越精美了,好看。
标签聚合
Spring JAVA K8s SQL Claude Redis JVM 设计模式 分布式 MySQL IDEA 架构 ElasticSearch 日常 SpringBoot AI编程 AI 数据库 WordPress 多线程
友情链接
  • Serendipity
  • 老张博客
  • 韩小韩博客
  • Mr.Sun的博客
  • 九仞之行
  • 志文工作室
  • 风渡言
  • 知向前端
  • 拾趣博客导航
  • Honesty
  • 瓦匠个人小站
  • 旧时繁华
  • 彬红茶日记
  • 懋和道人
  • 临窗旋墨
  • 搬砖日记
  • 林羽凡
  • 哥斯拉
  • 韩情脉脉
  • 皮皮社

COPYRIGHT © 2026 lifengdi.com. ALL RIGHTS RESERVED.

正在博友圈履约中

域名年龄

Theme Kratos+ By Dylan Li

津ICP备2024022503号-3

京公网安备11011502039375号