CPU 使用率过高排查:Redis 到底在忙什么
为什么 CPU 使用率过高需要排查
Redis 是单线程模型,CPU 是它的关键资源。当 CPU 使用率过高时:
– QPS 会达到上限,不能再提升
– 延迟可能显著增加
– 部分请求可能超时
– 如果使用多线程 IO(Redis 6.0+),CPU 使用率可能来自 IO 线程
第一步:确认 CPU 确实过高
# 查看 Redis 进程 CPU 使用率
top -p $(pgrep -x redis-server)
# 输出示例
top - 10:00:00 up 10 days, 2:35, 1 user, load average: 1.00
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 redis 20 0 2.100g 1.000g 12000 R 98.0 12.5 123:45.67 redis-server
解读:%CPU = 98%,说明 Redis 已经吃满了单核。
# 查看整体 CPU 使用
mpstat -P ALL 2 3
第二步:分类排查 CPU 消耗来源
来源一:正常业务 QPS 过高
redis-cli INFO STATS | grep instantaneous_ops_per_sec
# instantaneous_ops_per_sec:85000
如果是正常业务导致的高 QPS,且 CPU 已到极限,说明需要扩容了。
判断:
– QPS 在持续上升(业务增长正常)
– 没有明显慢查询
– 延迟在可接受范围
解决方案:
1. 使用 Redis Cluster 分片
2. 使用读写分离(主写从读)
3. 升级更高主频的 CPU
来源二:慢命令消耗 CPU
# 查看慢查询
redis-cli SLOWLOG GET 20
# 查看命令统计(找出 CPU 消耗大户)
redis-cli INFO COMMANDSTATS
# cmdstat_keys:calls=100,usec=5000000,usec_per_call=50000.00
# cmdstat_smembers:calls=200,usec=3000000,usec_per_call=15000.00
CPU 密集型命令排行榜:
| 命令 | 时间复杂度 | CPU 含量 | 优化方案 |
|---|---|---|---|
| KEYS | O(N) | ⭐⭐⭐⭐⭐ | 改用 SCAN |
| SMEMBERS | O(N) | ⭐⭐⭐⭐ | 改用 SSCAN |
| SORT | O(N+M*log(M)) | ⭐⭐⭐⭐ | 改用 zset |
| ZRANGE large | O(log(N)+M) | ⭐⭐⭐ | 限制返回数量 |
| LREM | O(N) | ⭐⭐⭐ | 使用其他结构 |
| DEL bigkey | O(N) | ⭐⭐⭐ | 改用 UNLINK |
来源三:频繁 Fork 导致 CPU 飙升
# 查看 fork 耗时
redis-cli INFO STATS | grep latest_fork_usec
# 查看 RDB 和 AOF 重写状态
redis-cli INFO PERSISTENCE
症状:
– CPU 周期性飙高
– 飙高时间点与 BGSAVE/AOF rewrite 时间吻合
原因:fork() 创建子进程时需要复制页表,如果内存占用大(数 GB),fork 过程会消耗大量 CPU。
# 检查 fork 对 CPU 的影响
import redis
r = redis.Redis()
def check_fork_cpu(r):
stats = r.info('stats')
persistence = r.info('persistence')
fork_time = stats.get('latest_fork_usec', 0) / 1000000
print(f"最近 fork 耗时: {fork_time:.2f}s")
bgsave_time = persistence.get('rdb_last_bgsave_time_sec', 0)
print(f"最近 BGSAVE 耗时: {bgsave_time}s")
if fork_time > 0.3:
print("⚠️ fork 耗时过长,建议关闭 THP")
来源四:过期 key 清理
# 查看过期和淘汰统计
redis-cli INFO STATS | grep -E "expired_keys|evicted_keys"
# 查看有 TTL 的 key 数量
redis-cli INFO KEYSPACE
# db0:keys=1000000,expires=500000,avg_ttl=3600
如果大量 key 在同一时间点过期,Redis 会密集清理过期 key,消耗 CPU。
# 检查是否密集过期
r = redis.Redis()
info = r.info('stats')
expired = info['expired_keys']
# 对比前后差值,如果每秒过期数 > 10000,需要优化
来源五:AOF 重写
# 查看 AOF 重写状态
redis-cli INFO PERSISTENCE
# aof_rewrite_in_progress:1 # 正在重写
# aof_last_rewrite_time_sec:30 # 上次重写耗时
AOF 重写本身由子进程执行,但主进程需要用 CPU 写入重写缓冲区(rewrite buffer)。如果写入量很大,CPU 消耗也会可观。
来源六:主动防御(安全原因)
# 检查是否有安全扫描或攻击
redis-cli CLIENT LIST
# 查看是否有异常 IP 大量连接
redis-cli CLIENT LIST | awk '{print $2}' | sort | uniq -c | sort -rn
工具链:从发现到定位
#!/bin/bash
# cpu_debug.sh - Redis CPU 问题排查脚本
echo "=== 当前 QPS ==="
redis-cli INFO STATS | grep instantaneous_ops
echo "=== 命令耗时 TOP ==="
redis-cli INFO COMMANDSTATS | sort -t: -k3 -rn | head -10
echo "=== 最近慢查询 ==="
redis-cli SLOWLOG GET 10 | grep -A 4 "4) \""
echo "=== Fork 耗时 ==="
redis-cli INFO STATS | grep latest_fork
echo "=== 连接数 ==="
redis-cli INFO CLIENTS | grep connected_clients
echo "=== 过期 key 情况 ==="
redis-cli INFO KEYSPACE
echo "过期/秒:"
redis-cli INFO STATS | grep expired_keys
echo "=== 系统级 ==="
strace -c -p $(pgrep -x redis-server) 2>&1 | head -20
分场景解决方案
| 场景 | 原因 | 解决方案 |
|---|---|---|
| 高 QPS + CPU 满 | 业务增长 | 集群分片/读写分离/升级 CPU |
| QPS 不高但 CPU 满 | 慢命令 | 检查 SLOWLOG 和 COMMANDSTATS |
| 周期性 CPU 飙高 | Fork/BGSAVE | 关闭 THP/调整持久化策略 |
| CPU 突然升高 | 攻击或异常流量 | 检查 CLIENT LIST |
| CPU 缓慢增长 | key 持续增加 | 数据分片/清理过期数据 |
预防措施
# 监控并设置 CPU 告警
import psutil
import redis
def monitor_cpu(redis_host='localhost', threshold=80):
"""监控 Redis CPU 使用率"""
r = redis.Redis(host=redis_host)
while True:
pid = r.info('server').get('process_id', 0)
try:
proc = psutil.Process(pid)
cpu_pct = proc.cpu_percent()
if cpu_pct > threshold:
qps = r.info('stats')['instantaneous_ops_per_sec']
slowlogs = r.slowlog_get(5)
alert_message = (
f"Redis CPU 告警: {cpu_pct}%\n"
f"当前 QPS: {qps}\n"
f"慢查询: {[s['command'] for s in slowlogs]}"
)
print(alert_message)
# 发送告警...
except (psutil.NoSuchProcess, Exception) as e:
print(f"监控异常: {e}")
time.sleep(10)
面试要点
- CPU 高不一定是 Redis 慢——可能是 正常业务负载高
- SLOWLOG 和 COMMANDSTATS 是定位 CPU 消耗的首选工具
- 最高 CPU 消耗的场景往往是 KEYS/SMEMBERS 等 O(N) 命令
- THP 未关闭 导致 fork 时 CPU 飙升
- 大量 key 同时过期也会导致 CPU 飙高
- 在确定要扩容前,先确认是否可以通过优化命令来解决
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容