线上 Redis 服务 CPU 占用很高该如何排查和解决?
问题现象:当 Redis "发烧" 时会发生什么?
想象一下,当 Redis 服务器 CPU 使用率飙升时,就像一个人发高烧一样,整个系统都会出现各种不适症状:
🚨 性能下降的连锁反应
- 响应变慢:原本毫秒级的操作突然变成秒级,用户明显感受到卡顿
- 吞吐量暴跌:QPS 从几万降到几千,系统处理能力大幅下降
- 连接超时:客户端开始频繁报错,连接池耗尽
- 系统负载飙升:整个服务器负载过高,影响其他服务
💡 实际场景举例
用户反馈:页面加载很慢,经常超时
监控告警:Redis CPU 使用率 95%,响应时间 > 1000ms
业务影响:订单处理失败率上升,用户体验严重下降
常见原因分析:找出"罪魁祸首"
1. 复杂查询操作:Redis 的"重体力活"
🐌 慢查询 - 时间杀手
- 某些命令执行时间过长,就像让 Redis 做"马拉松"而不是"短跑"
- 常见场景:
KEYS *命令扫描整个数据库,SORT命令对大量数据进行排序 - 影响:单个慢查询会阻塞整个 Redis 实例,影响所有客户端
🐘 大 key 操作 - 内存巨兽
- 单个 key 存储的数据过大(通常 > 1MB),操作时消耗大量 CPU
- 典型例子:存储整个用户列表的 Hash,包含百万条记录的 List
- 问题:每次操作都要处理大量数据,CPU 使用率飙升
🧮 复杂计算 - Lua 脚本的陷阱
- Lua 脚本中执行复杂逻辑,占用过多 CPU 时间
- 危险操作:在脚本中进行大量循环、字符串处理、数学计算
- 后果:脚本执行时间过长,阻塞其他操作
2. 频繁的键空间操作:Redis 的"家务活"
🧹 大量过期键清理
- Redis 需要定期清理过期的 key,这个过程会消耗 CPU
- 当有过期 key 数量巨大时,清理工作变得繁重
- 症状:CPU 使用率周期性波动,清理时达到峰值
🗑️ 内存淘汰机制
- 当内存不足时,Redis 需要选择并删除一些 key
- 淘汰策略越复杂,CPU 消耗越大
- 影响:每次淘汰都要遍历和比较大量数据
📢 键空间通知
- 监听 key 的变化事件,每个操作都可能触发通知
- 大量通知会消耗额外的 CPU 资源
- 问题:通知处理逻辑复杂时,CPU 使用率上升
3. 网络 I/O 问题:Redis 的"交通堵塞"
🚗 客户端连接过多
- 大量客户端同时连接,每个连接都需要维护状态
- 连接数过多时,Redis 需要处理更多的网络事件
- 影响:网络 I/O 成为瓶颈,CPU 忙于处理连接
🌐 网络延迟问题
- 网络不稳定导致数据包丢失,需要重传
- 高延迟环境下,Redis 等待网络响应时间过长
- 结果:CPU 空闲等待,但整体性能下降
🔄 序列化/反序列化开销
- 客户端和 Redis 之间的数据需要编解码
- 复杂数据结构序列化消耗更多 CPU
- 优化:使用更高效的序列化格式(如 MessagePack)
4. 配置问题:Redis 的"设置不当"
💾 持久化配置不当
- RDB 快照生成时消耗大量 CPU,影响正常服务
- AOF 重写过程复杂,占用过多资源
- 解决:调整持久化策略,避免在高峰期进行
🧠 内存配置错误
- maxmemory 设置过小,频繁触发内存淘汰
- 没有设置合适的淘汰策略,导致性能问题
- 建议:根据实际使用情况合理配置内存限制
⚙️ 线程配置问题
- Redis 6.0 之前是单线程模型,所有操作串行执行
- 没有启用多线程 I/O,网络处理成为瓶颈
- 升级:使用 Redis 6.0+ 并启用多线程 I/O
排查步骤:像医生诊断一样系统排查
1. 监控 CPU 使用情况:给 Redis 做"体检"
🔍 第一步:查看整体健康状况
# 查看Redis进程CPU使用率(就像量体温)
top -p $(pgrep redis-server)
# 使用htop查看详细CPU信息(更直观的体检报告)
htop
# 查看Redis实时统计信息(获取详细的生命体征)
redis-cli info stats
💡 诊断要点:
- CPU 使用率持续 > 80% 说明有问题
- 如果 CPU 使用率波动很大,可能是周期性任务导致
- 查看
used_cpu_sys和used_cpu_user了解 CPU 消耗分布
📊 实际案例:
# 正常情况下的输出示例
used_cpu_sys:1234.56
used_cpu_user:2345.67
used_cpu_sys_children:12.34
used_cpu_user_children:23.45
# 异常情况:CPU使用率过高
used_cpu_sys:9999.99 # 系统CPU使用率异常高
used_cpu_user:8888.88 # 用户CPU使用率异常高
2. 分析慢查询日志:找出"慢性病"
📊 开启慢查询监控
# 设置慢查询阈值(微秒)- 就像设置血压计
redis-cli config set slowlog-log-slower-than 10000 # 10毫秒
redis-cli config set slowlog-max-len 128 # 保存128条记录
# 查看最近的慢查询(查看病历)
redis-cli slowlog get 10
# 查看慢查询日志长度(统计病例数)
redis-cli slowlog len
🔍 分析技巧:
- 重点关注执行时间最长的命令
- 查看命令参数,找出大 key 或复杂操作
- 统计慢查询频率,判断是否为持续性问题
🚨 慢查询分析示例:
# 查看慢查询日志
redis-cli slowlog get 5
# 输出示例:
1) 1) (integer) 1234567890 # 唯一ID
2) (integer) 1234567890 # 执行时间戳
3) (integer) 50000 # 执行耗时(微秒)
4) 1) "KEYS" # 命令
2) "*" # 参数
5) "127.0.0.1:12345" # 客户端地址
6) "" # 客户端名称
# 这个例子显示KEYS *命令执行了50毫秒,这是非常慢的!
3. 检查内存使用情况:排查"营养过剩"
🧠 内存使用分析
# 查看内存使用情况(检查体重)
redis-cli info memory
# 查找大key(找出"肥胖"的数据)
redis-cli --bigkeys
# 查看键空间信息(了解数据分布)
redis-cli info keyspace
🎯 重点关注指标:
used_memory_percentage:内存使用率maxmemory:最大内存限制evicted_keys:被淘汰的 key 数量- 大 key 的大小和类型
📈 内存分析示例:
# 查看内存使用情况
redis-cli info memory | grep -E "(used_memory|maxmemory|evicted_keys)"
# 输出示例:
used_memory:1073741824 # 已使用内存:1GB
used_memory_percentage:85.5 # 内存使用率:85.5%
maxmemory:1342177280 # 最大内存:1.25GB
evicted_keys:1234 # 被淘汰的key数量:1234个
# 这个例子显示内存使用率已经很高,需要关注
4. 分析客户端连接:检查"社交圈"
👥 连接状态分析
# 查看所有客户端连接(查看朋友圈)
redis-cli client list
# 查看连接统计(统计社交活跃度)
redis-cli info clients
# 查看命令执行统计(了解工作强度)
redis-cli info commandstats
📈 关键指标解读:
connected_clients:当前连接数blocked_clients:被阻塞的客户端数- 各命令的执行次数和耗时统计
- 客户端 IP 分布,识别异常连接
🔍 连接分析示例:
# 查看客户端连接
redis-cli client list
# 输出示例:
id=123 addr=127.0.0.1:12345 fd=8 name= age=1234 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
id=124 addr=127.0.0.1:12346 fd=9 name= age=1233 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
# 这个例子显示有两个客户端连接,都在执行ping和get命令
5. 深度分析:使用专业工具
🛠️ Redis 内置分析工具
# 使用Redis自带的性能分析工具
redis-cli --latency-history -i 1
# 监控Redis实时性能
redis-cli monitor
# 分析Redis配置
redis-cli config get "*"
📊 第三方监控工具
# 使用redis-cli的统计功能
redis-cli --stat
# 分析Redis内存使用
redis-cli --memkeys
# 查看Redis配置建议
redis-cli --check-config
优化方案:对症下药,精准治疗
1. 查询优化:让 Redis 跑得更快
🚀 避免慢查询 - 从源头解决问题
# 设置合理的慢查询阈值(微秒)
redis-cli config set slowlog-log-slower-than 10000
# 定期分析慢查询日志,找出问题命令
redis-cli slowlog get 100 | head -20
# 实时监控慢查询
watch -n 1 'redis-cli slowlog len'
💡 慢查询优化技巧:
- 用
SCAN替代KEYS命令,避免阻塞 - 使用
EXISTS替代GET检查 key 是否存在 - 合理使用
SORT命令的LIMIT参数 - 避免在 Lua 脚本中执行复杂逻辑
🐘 优化大 key 操作 - 分而治之
# 查找大key(找出问题数据)
redis-cli --bigkeys
# 分批处理大key
# 使用SCAN命令替代KEYS命令
redis-cli --scan --pattern "user:*" | head -100
# 分析大key的内存使用
redis-cli memory usage key_name
🔧 大 key 优化策略:
- Hash 大 key:拆分成多个小 Hash
- List 大 key:使用分片存储
- Set 大 key:考虑使用 HyperLogLog
- String 大 key:压缩存储或拆分
🧮 优化 Lua 脚本 - 精简高效
-- ❌ 避免:在脚本中进行复杂计算
local result = {}
for i = 1, 1000000 do
result[i] = math.sqrt(i) * math.sin(i)
end
-- ✅ 推荐:使用Redis内置命令
redis.call('SADD', 'processed_keys', key)
redis.call('EXPIRE', key, 3600)
📝 Lua 脚本优化原则:
- 避免在脚本中进行大量循环
- 使用 Redis 内置命令替代自定义逻辑
- 限制脚本执行时间
- 使用
redis.pcall处理可能的错误
2. 内存优化:让 Redis 更"苗条"
⏰ 合理设置过期时间 - 自动清理
# 为键设置合理的过期时间
redis-cli expire key_name 3600
# 使用EXPIREAT设置绝对过期时间
redis-cli expireat key_name 1640995200
# 批量设置过期时间
redis-cli eval "
for i = 1, 1000 do
redis.call('EXPIRE', 'key' .. i, 3600)
end
" 0
🎯 过期时间设置策略:
- 根据业务需求设置合理的 TTL
- 避免所有 key 同时过期(缓存雪崩)
- 使用随机过期时间分散清理压力
- 定期检查过期 key 的分布
🗑️ 优化内存淘汰策略 - 智能清理
# 查看当前淘汰策略
redis-cli config get maxmemory-policy
# 设置合适的淘汰策略
redis-cli config set maxmemory-policy allkeys-lru
# 设置最大内存限制
redis-cli config set maxmemory 2gb
# 查看淘汰统计
redis-cli info stats | grep evicted
📊 淘汰策略选择指南:
- allkeys-lru:适合大多数场景,基于最近最少使用
- volatile-lru:只淘汰有过期时间的 key
- allkeys-random:随机淘汰,适合 key 访问模式均匀的场景
- volatile-ttl:优先淘汰即将过期的 key
3. 网络优化:让 Redis 连接更顺畅
🔗 连接池优化 - 合理管理连接
// Java客户端连接池配置示例
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100); // 最大连接数
poolConfig.setMaxIdle(20); // 最大空闲连接
poolConfig.setMinIdle(5); // 最小空闲连接
poolConfig.setMaxWaitMillis(3000); // 最大等待时间
poolConfig.setTestOnBorrow(true); // 获取连接时测试
poolConfig.setTestOnReturn(true); // 归还连接时测试
poolConfig.setTestWhileIdle(true); // 空闲时测试连接
⚡ 批量操作优化 - 减少网络往返
# 使用pipeline批量操作
redis-cli --pipe < commands.txt
# 使用multi/exec事务
redis-cli multi
redis-cli set key1 value1
redis-cli set key2 value2
redis-cli set key3 value3
redis-cli exec
# 使用Lua脚本批量操作
redis-cli eval "
for i = 1, 1000 do
redis.call('SET', 'key' .. i, 'value' .. i)
end
" 0
🌐 网络优化技巧:
- 使用连接池避免频繁创建连接
- 批量操作减少网络往返次数
- 使用 pipeline 提高吞吐量
- 合理设置超时时间
4. 配置优化:让 Redis 运行更稳定
💾 持久化配置优化 - 平衡性能与安全
# RDB配置优化(根据业务需求调整)
redis-cli config set save "900 1 300 10 60 10000"
# AOF配置优化
redis-cli config set appendfsync everysec
redis-cli config set no-appendfsync-on-rewrite yes
redis-cli config set auto-aof-rewrite-percentage 100
redis-cli config set auto-aof-rewrite-min-size 64mb
# 混合持久化(Redis 4.0+)
redis-cli config set aof-use-rdb-preamble yes
⚙️ 线程配置优化 - 充分利用多核
# Redis 6.0+ 多线程配置
redis-cli config set io-threads 4
redis-cli config set io-threads-do-reads yes
# 查看线程配置
redis-cli config get io-threads*
🔧 其他重要配置
# 调整TCP连接参数
redis-cli config set tcp-keepalive 60
redis-cli config set timeout 300
# 调整客户端连接数
redis-cli config set maxclients 10000
# 调整内存使用
redis-cli config set maxmemory 2gb
redis-cli config set maxmemory-policy allkeys-lru
监控和告警:建立完善的健康监控体系
1. 关键指标监控:实时掌握 Redis 健康状况
📊 核心性能指标
# CPU使用率监控
redis-cli info stats | grep used_cpu_sys
redis-cli info stats | grep used_cpu_user
# 内存使用率监控
redis-cli info memory | grep used_memory_percentage
redis-cli info memory | grep maxmemory
# 连接数监控
redis-cli info clients | grep connected_clients
redis-cli info clients | grep blocked_clients
# 命令执行统计
redis-cli info commandstats | head -20
⏱️ 延迟监控
# 监控Redis延迟
redis-cli --latency-history -i 1
# 监控Redis延迟分布
redis-cli --latency-dist
# 监控Redis延迟趋势
redis-cli --latency
📈 吞吐量监控
# 监控Redis统计信息
redis-cli --stat
# 监控Redis实时性能
redis-cli monitor | head -100
2. 告警阈值设置:及时发现问题
🚨 关键告警指标
- CPU 使用率 > 80%:CPU 使用率过高
- 内存使用率 > 90%:内存使用率过高
- 连接数 > 1000:连接数过多
- 慢查询数量 > 100/分钟:慢查询过多
- 响应时间 > 100ms:响应时间过长
- 键空间命中率 < 95%:缓存命中率过低
📱 告警配置示例
# 设置慢查询告警
redis-cli config set slowlog-log-slower-than 5000
# 设置内存告警
redis-cli config set maxmemory 2gb
redis-cli config set maxmemory-policy allkeys-lru
# 设置连接数告警
redis-cli config set maxclients 1000
3. 监控工具推荐:选择合适的监控方案
🛠️ 官方工具
- RedisInsight:Redis 官方可视化工具,功能强大
- redis-cli:命令行工具,适合脚本化监控
- Redis Sentinel:高可用监控工具
📊 第三方监控工具
- Redis Exporter + Prometheus:专业监控指标收集
- Grafana:美观的监控面板展示
- Zabbix:企业级监控方案
- Nagios:传统监控工具
- Datadog:云监控服务
💡 监控工具选择建议
- 小团队:RedisInsight + 简单脚本
- 中型团队:Prometheus + Grafana + Redis Exporter
- 大型团队:Zabbix + 自定义监控脚本
- 云环境:云服务商提供的监控服务
应急处理:快速响应,减少损失
1. 立即缓解措施:紧急止血
🚑 第一步:快速诊断
# 1. 查看当前状态
redis-cli info stats
# 2. 检查慢查询
redis-cli slowlog get 10
# 3. 查看内存使用
redis-cli info memory
# 4. 检查连接数
redis-cli info clients
⚡ 第二步:临时缓解
# 1. 临时降低慢查询阈值
redis-cli config set slowlog-log-slower-than 1000
# 2. 清理慢查询日志
redis-cli slowlog reset
# 3. 临时调整内存淘汰策略
redis-cli config set maxmemory-policy allkeys-lru
# 4. 临时限制客户端连接
redis-cli config set maxclients 100
🔄 第三步:重启服务(谨慎操作)
# 1. 保存当前配置
redis-cli config get "*" > redis_config_backup.txt
# 2. 重启Redis服务
sudo systemctl restart redis
# 3. 验证服务状态
redis-cli ping
2. 临时优化:快速改善性能
💾 临时禁用持久化(数据丢失风险)
# 临时禁用RDB
redis-cli config set save ""
# 临时禁用AOF
redis-cli config set appendonly no
# 注意:这会增加数据丢失风险,仅在紧急情况下使用
🧠 临时调整内存策略
# 临时调整内存淘汰策略
redis-cli config set maxmemory-policy allkeys-lru
# 临时增加内存限制
redis-cli config set maxmemory 4gb
🔧 临时优化网络
# 临时调整超时设置
redis-cli config set timeout 60
# 临时调整TCP设置
redis-cli config set tcp-keepalive 30
3. 应急处理流程:标准化响应
📋 应急处理检查清单
- 立即响应:确认问题影响范围
- 快速诊断:使用监控工具定位问题
- 临时缓解:采取紧急措施降低影响
- 深入分析:找出根本原因
- 制定方案:制定长期解决方案
- 实施修复:执行优化措施
- 验证效果:确认问题已解决
- 总结经验:记录处理过程和经验
📞 应急联系方式
- 技术负责人:负责技术决策
- 运维团队:负责服务器操作
- 业务团队:负责业务影响评估
- 监控团队:负责监控告警
预防措施:未雨绸缪,防患于未然
1. 定期维护:保持 Redis 健康状态
📅 日常维护任务
- 每日:检查 CPU 和内存使用率
- 每周:分析慢查询日志
- 每月:检查大 key 和过期 key 分布
- 每季度:优化数据结构和索引
- 每年:更新 Redis 版本和配置
🔍 定期检查脚本
#!/bin/bash
# Redis健康检查脚本
echo "=== Redis健康检查 ==="
echo "时间: $(date)"
echo ""
echo "1. CPU使用率:"
redis-cli info stats | grep used_cpu
echo "2. 内存使用率:"
redis-cli info memory | grep used_memory_percentage
echo "3. 连接数:"
redis-cli info clients | grep connected_clients
echo "4. 慢查询数量:"
redis-cli slowlog len
echo "5. 大key检查:"
redis-cli --bigkeys | head -10
echo "6. 键空间信息:"
redis-cli info keyspace
2. 容量规划:为未来做好准备
📊 容量规划要素
- 当前使用量:CPU、内存、连接数
- 业务增长预测:用户增长、数据增长
- 性能要求:响应时间、吞吐量
- 可用性要求:故障恢复时间
📈 容量规划方法
# 1. 收集历史数据
redis-cli info stats > redis_stats_$(date +%Y%m%d).txt
# 2. 分析增长趋势
# 使用监控工具分析历史数据
# 3. 预测未来需求
# 根据业务增长预测Redis资源需求
# 4. 制定扩容计划
# 制定分阶段的扩容方案
🎯 扩容策略
- 垂直扩容:增加单机配置
- 水平扩容:增加 Redis 实例
- 分片扩容:使用 Redis Cluster
- 读写分离:使用主从复制
3. 代码规范:从源头避免问题
📝 开发规范
- 避免使用 KEYS 命令:使用 SCAN 替代
- 合理使用过期时间:避免缓存雪崩
- 优化 Lua 脚本:避免复杂计算
- 使用连接池:避免频繁创建连接
- 批量操作:减少网络往返
🔍 代码审查要点
- 检查是否有慢查询命令
- 检查是否有大 key 操作
- 检查 Lua 脚本复杂度
- 检查连接池配置
- 检查错误处理逻辑
📚 培训计划
- 基础培训:Redis 基本概念和命令
- 性能培训:性能优化技巧
- 监控培训:监控工具使用
- 故障处理培训:应急处理流程
总结:让 Redis 始终保持最佳状态
Redis CPU 占用过高通常是由复杂查询、大 key 操作、内存问题或配置不当引起的。通过系统性的排查和优化,可以有效解决 CPU 占用过高的问题。
🎯 关键成功要素:
- 完善的监控体系:及时发现和预警问题
- 系统性的排查方法:快速定位问题根源
- 针对性的优化措施:有效解决具体问题
- 预防性的维护机制:避免问题再次发生
- 标准化的应急流程:快速响应和处理问题
💡 最佳实践建议:
- 建立完善的监控告警体系
- 定期进行性能分析和优化
- 制定标准化的运维流程
- 加强团队技术培训和能力建设
- 建立问题处理的知识库
🚀 持续改进:
- 定期回顾和优化监控指标
- 持续改进故障处理流程
- 不断学习新的优化技术
- 分享经验和最佳实践
通过以上系统性的方法,我们可以让 Redis 始终保持最佳状态,为业务提供稳定、高效的数据服务。
