redis cpu过高需先用top -hp查线程分布,主线程高占比说明命令执行瓶颈;再通过info stats、slowlog、monitor定位高频命令/热key/客户端;大key和lua脚本循环调用是隐性高cpu元凶。

Redis集群CPU过高,不能只看top里redis-server进程的占用率——它掩盖了真正耗CPU的是哪个命令、哪个key、甚至哪个客户端连接。单靠top只能确认“是Redis在吃CPU”,但无法区分是EVAL脚本卡死、KEYS *全库扫描、还是成千上万短连接反复释放导致的listSearchKey高频调用。
先用top确认是不是Redis本身,再进进程看线程分布
执行top -Hp [redis_pid](不是top,必须加-H),观察各线程的%CPU。Redis主线程(通常ID最小)占比若超80%,说明瓶颈在命令执行层;若多个线程均匀占高,可能是IO线程或AOF重写线程在刷盘。注意:Redis 6+默认启用io-threads,但仅用于读写网络缓冲区,命令执行仍由主线程串行处理——所以CPU飙高90%时,主线程几乎必然100%占用。
常见误判点:
- 没加
-H直接top,看到redis-server整体95%,就以为是“Redis慢”,实际可能是后台fork子进程做RDB导致的瞬时抖动(此时主线程%CPU反而不高) - 在容器环境用
top看到CPU高,但未检查是否被cpu.shares或cpusets限制,导致Redis线程争抢激烈
用info stats和slowlog快速筛出高开销命令
连接到疑似高负载节点(优先连从节点,避免加重主节点压力),执行:
redis-cli -h [host] -p [port] -a [pass] info stats | grep -E "(instantaneous_ops_per_sec|rejected_connections|expired_keys)"
重点看instantaneous_ops_per_sec:持续超过5万且伴随CPU高,大概率是QPS压爆;若只有几百但CPU仍高,问题一定出在单条命令复杂度上。
接着查慢日志:
redis-cli -h [host] -p [port] -a [pass] config set slowlog-log-slower-than 5000<br>redis-cli -h [host] -p [port] -a [pass] slowlog get 50
重点关注这几类命令:
-
KEYS *、SCAN未带COUNT参数(默认COUNT 10,但业务误用成SCAN 0 COUNT 10000也会卡) -
HGETALL、SMEMBERS、ZRANGE key 0 -1——数据量超1k时,O(n)开销明显 -
EVAL或EVALSHA后跟着长Lua脚本,尤其含for循环或redis.call("GET", ...)嵌套调用
用monitor抓实时命令流,定位Hotkey和高频客户端
monitor有性能代价,必须控制时长和范围:
timeout 3s redis-cli -h [slave_host] -p [slave_port] -a [pass] monitor > /tmp/mon.log 2>/dev/null
3秒足够捕获数百条命令。之后分析:
- 找高频
EVAL对应的key:grep -i 'eval' /tmp/mon.log | sed 's/\"//g' | cut -d' ' -f3 | sort | uniq -c | sort -nr | head -10 - 找高频访问的key(不区分读写):
awk '{print $3}' /tmp/mon.log | sort | uniq -c | sort -nr | head -10 - 按客户端IP聚合:
awk '{print $1}' /tmp/mon.log | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]+' | sort | uniq -c | sort -nr
注意:monitor输出中"127.0.0.1:54321"是客户端地址,但字段位置不固定,建议用grep -oE提取IP+端口更稳。
用memory usage和object freq验证大Key与热Key
对上一步发现的可疑key,逐个检查内存占用和访问频次:
redis-cli -h [host] -p [port] -a [pass] memory usage "user:1001"<br>redis-cli -h [host] -p [port] -a [pass] object freq "user:1001"
object freq返回0–255的热度值(基于LFU算法),值≥100基本可判定为热Key;memory usage若超1MB,需警惕——大Key不仅占内存,序列化/反序列化也吃CPU。
特别提醒:object freq在Redis 4.0+才支持,且需配置maxmemory-policy allkeys-lfu或volatile-lfu才有效;若返回(nil),说明该key未被LFU策略追踪,此时应改用monitor或客户端埋点验证。
真正难排查的,往往不是KEYS *这种显性炸弹,而是某个Lua脚本里for i=1,5000 do redis.call("HGET", KEYS[1], ARGV[i]) end这类“合法但致命”的写法——它不会出现在slowlog里(因为每条HGET都很快),但5000次调用叠加后,主线程完全被锁死。这种场景下,perf top -p [pid]里call函数栈占比会异常高,必须结合monitor原始命令流才能还原现场。











