1 . 前置检查:确认 CPU 真的是瓶颈
在正式打性能“补丁”前,务必跑一遍系统级健康核对表(推荐 Brendan Greg 的 USE Method):
资源 | 关注指标 | 常用工具 |
---|---|---|
CPU | Util/Idle、RunQueue | top 、vmstat 、sar |
内存 | Fault、Swap、Cache Miss | free 、perf stat |
I/O | IOPS、Latency、Wait | iostat 、pidstat -d |
网络 | PPS、Rtt、Drop | ifstat 、ss |
当你确认 Redis 主要耗时在 CPU on-cpu,再继续以下步骤。
2 . 为可追踪重新编译 Redis
目标:既保留
-O2
优化获得真实的生产时序,又保证栈帧可解析。
# 从 Redis 源码根目录编译
make clean
make REDIS_CFLAGS="-g -fno-omit-frame-pointer"
-g
: 保留调试符号,便于栈回溯-fno-omit-frame-pointer
: 强制保留 FP 寄存器-O2
: 维持 Release 优化级别
3 . 热点采样(Hotspot Analysis)
3.1 使用 perf 采样用户态 + 内核态栈
# 60 s 内以 999 Hz 频率采样 redis-server 进程
perf record -g -F 999 --pid $(pgrep redis-server) -- sleep 60
分析报告
perf report -g "graph,0.5,caller"
graph
展示调用关系0.5
截断阈值(≥0.5 %)caller
自上而下查看“谁调用了热点函数”
3.2 使用 eBPF/BCC 的 profile 工具
内核 4.9+ 之后,BPF 方案可在内核空间折叠栈,再回写到用户态,极大降低开销。
/usr/share/bcc/tools/profile -F 999 -f \--pid $(pgrep redis-server) --duration 60 > redis.folded.stacks
一步到位获得 folded 格式(形如 main;call;dictFind 12
),后续直接生成火焰图。
4 . 可视化:生成交互式 Flame Graph
# perf 采样
perf script > redis.perf.stacks
stackcollapse-perf.pl redis.perf.stacks > redis.folded.stacks# 或 bcc 已直接输出 folded
flamegraph.pl redis.folded.stacks > redis.svg
- 横轴 = 调用栈聚合后 样本占比
- 纵轴 = 调用深度
- 点击 SVG 方块可下钻到更深层栈帧
5 . 调用频次分析(Call Counts)
高 CPU 占用可能是 单次调用很慢,也可能是 调用极其频繁。用 BCC 的 funccount
快速把函数调用次数统计出来:
/usr/share/bcc/tools/funccount 'redis-server:(call*|*Read*|*Write*)' \--pid $(pgrep redis-server) --duration 60
输出示例:
FUNC COUNT
call 334
handleClientsWithPendingWrites 388
prepareClientToWrite 1442
配合热点栈信息,就能判断该优化“减肥”还是“减次数”。
6 . 硬件事件采样:PMCs 诊断失速
通过
perf stat
一次性拉齐真正的 CPU IPC、Cache Miss 与 Stall 周期。
perf stat -e \cpu-clock,cpu-cycles,instructions, \uops_executed.core,uops_executed.stall_cycles, \cache-references,cache-misses, \cycle_activity.stalls_l1d_miss,cycle_activity.stalls_l2_miss, \cycle_activity.stalls_l3_miss \--pid $(pgrep redis-server) -- sleep 60
关键指标解读:
指标 | 理想 | 诊断思路 |
---|---|---|
IPC (instructions / cycles ) | > 2(现代 x86) | 低则说明管线空转或 Cache Miss |
cache-miss / cache-ref | < 3 % | > 10 % 多半缓存友好度差 |
stall_cycles / cycles | 越低越好 | 高说明 CPU 在等数据 |
7 . 一键脚本示例
下面脚本可把 采样 + 折叠 + FlameGraph 合并执行,适合 CI / 回归测试:
#!/usr/bin/env bash
PID=$(pgrep redis-server)
DUR=60
FREQ=999
OUT=/tmp/redis_$(date +%s)# 1) 采样
perf record -g -F $FREQ --pid $PID -- sleep $DUR# 2) 转栈
perf script > $OUT.stacks
stackcollapse-perf.pl $OUT.stacks > $OUT.folded# 3) 火焰图
flamegraph.pl $OUT.folded > $OUT.svg
echo "🔥 FlameGraph ready: $OUT.svg"
8 . 生产环境落地建议
- 采样频率:
999 Hz
已覆盖绝大多数业务;异常抖动场景可降到199 Hz
减少开销。 - 采样窗口:确保覆盖 GC、AOF rewrite、慢查询 等高峰时段。
- 分段对比:升级或改代码前后做 差分 FlameGraph,一眼看出新增/消失的热点。
- 自动归档:用
perf-archive.sh
+tar
保存perf.data
与 Build-ID,方便异地复盘。 - 代码热补丁:热点定位后,可先试
serverCron
、dictFind
等典型函数的算法或数据结构优化,再决定是否拆分 RDB、AOF 线程。
9 . 常见疑难解答
症状 | 排查点 |
---|---|
FlameGraph 栈帧只有不到两层 | 未加 -fno-omit-frame-pointer 或 strip 了 debug 符号 |
perf record 提示 “PERF_EVENT_OPEN failed” | 当前用户无 CAP_SYS_ADMIN ,用 sudo 或调整 kernel.perf_event_paranoid=-1 |
采样时 Redis 延迟抖高 | 频率过大 / 容器 cgroup 受限,先用 eBPF profile 或降低采样率 |
火焰图找不到内核函数 | 确保安装 kernel-debuginfo 或 linux-image-*-dbgsym |
结语
- perf + eBPF 解决“算在哪”的问题;
- PMCs 解决“慢在哪”的问题;
- 系统方法论 (USE / RED) 解决“该不该”的问题。
把三者串起来,就形成了 Redis CPU 优化的闭环:定位 → 可视化 → 改进 → 回归。
愿你下次再看火焰图时,只为欣赏那条平稳而美丽的线!