
21. 性能调优实战——redis-benchmark 坑点,100w qps 突破:绑核、NUMA、中断亲和
(接上一篇《20. 把 Redis 压到 60 w QPS 之后,CPU 为何突然“罢工”》)
一、现象回顾:60 w 之后“天花板”
上一篇我们把单实例 Redis 压到 60 w QPS 后,再提高 -c 线程数,QPS 反而掉到 45 w,CPU 利用率却锁死在 55 %。perf 一看,50 % 时间耗在 __inet_lookup_established 与 ksoftirqd,硬中断均衡失败,跨 NUMA 内存一跳 120 ns,上下文切 1.2 M/s。结论:不是 Redis 慢,是“送包子”的路被堵死。下面把踩过的 5 个坑、4 条命令、3 组参数、2 行代码、1 个脚本一次性摊开,带你把单实例 Redis 推到 100 w QPS(单条 16 Byte 读,P99 < 1 ms),并保证 8 核负载 95 % 仍线性可压。
二、redis-benchmark 的 5 个暗坑
| 坑 | 现象 | 根因 | 修复 |
|---|---|---|---|
| 1. 默认 -k 0 | 端口 TIME_WAIT 暴涨,3 w/s 新建连失败 | 每次短连,内核 3.5 k 本地端口瞬间耗尽 | -k 1 长连,或 sysctl net.ipv4.ip_local_port_range="1024 65535" |
| 2. 单线程 -c | 客户端成瓶颈,本机 CPU 100 % 但 QPS 不涨 | redis-benchmark 默认只开 1 线程 | -t 8 让客户端也 8 线程,与服务器对等 |
| 3. 默认 16 Byte payload | 网卡小包 PPS 先顶满,带宽 1 % | 512 Byte 以上才打带宽,16 Byte 纯 PPS | 压 QPS 用 16 B,压带宽用 1 kB |
| 4. 本机压本机 | 上下文切双倍,跨 NUMA 双倍 | 中断与 Redis 抢核 | 用两台同 NUMA 拓扑裸金属,10 GbE 直连 |
| 5. 忘记 –-csv | 结果只能肉眼 grep,无法画曲线 | 官方文档没提 | --csv -q 重定向到文件,gnuplot 秒出图 |
三、绑核:把“送包子”的专车道划出来
- 拓扑先看清
lscpu -e # 看物理核、NUMA 对应
cat /proc/interrupts | grep eth0 # 看网卡队列落在哪些 CPU
假设 2 NUMA、16 Core、32 HT,eth0 8 队列,0-7 号中断落在 NUMA0。
- Redis 绑 NUMA0 的 8-15 核
taskset -c 8-15 redis-server /etc/redis.conf
- 中断绑到 NUMA0 的 0-7 核,与 Redis 物理隔离
echo 0-7 > /proc/irq/24/smp_affinity_list # 24 是 eth0-TxRx-0
...
echo 0-7 > /proc/irq/31/smp_affinity_list
- 禁止 ksoftirqd 乱飘
echo 0-7 > /proc/sys/kernel/irqaffinity
- 客户端同样绑到对端 NUMA 的 8-15 核,保证“发”与“收”在同一条 PCIe 根复合体,跨 NUMA 跳数 0。
四、NUMA:别只绑核,内存也要“就近”
Redis 启动后再 numactl --membind 已经晚了,必须在启动时就把堆、栈、RDB 缓冲区全部钉在 NUMA0:
numactl --cpunodebind=0 --membind=0 redis-server /etc/redis.conf
验证:
cat /proc/$(pidof redis-server)/numa_maps | grep heap
应该只看到 N0=xxxx,N1 为 0;一旦出现 N1 > 0,说明有跨 Node 分配,QPS 掉 8 %。
五、中断亲和:让网卡队列与 Redis 线程“谈恋爱”
Linux 默认 irqbalance 会把队列均衡到所有核,结果 Redis 线程刚跑满,就被中断拽走。
- 关闭 irqbalance
systemctl stop irqbalance && systemctl disable irqbalance
- 手动 1:1 队列到隔离核
set_irq_affinity.sh 0-7 eth0 # 脚本来自 DPDK tools
- 打开 RPS,让软中断也能hash到 Redis 线程所在核
echo ffffff00 > /sys/class/net/eth0/queues/rx-0/rps_cpus # 8-15 位图
这样硬中断 0-7,软中断 8-15,Redis 线程 8-15,三层同一 NUMA,零跨跳。
六、内核参数:把最后 5 % 抠出来
# 端口回收
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_tw_buckets = 2000000
# 短连风暴
net.core.somaxconn = 65535
net.ipv4.tcp_abort_on_overflow = 1
# 小包 PPS
net.core.netdev_budget = 600
net.core.netdev_budget_usecs = 8000
重载:
sysctl -p /etc/sysctl.d/99-redis.conf
七、redis.conf 最后两刀
# 关闭保存,避免 fork 抖动
save ""
# 关闭 AOF,纯内存压测
appendonly no
# 把主线程绑到 8-15 核
server_cpulist 8-15
# 关闭 THP,防止 2 M 大页 NUMA 漂移
disable-thp yes
后两条在 Redis 7.0 以后才有,老版本用 echo never > /sys/kernel/mm/transparent_hugepage/enabled。
八、压测命令:一次到位
服务端:
numactl --cpunodebind=0 --membind=0 \
taskset -c 8-15 \
redis-server /etc/redis.conf --port 6379 --maxmemory 8gb
客户端(另一台同拓扑机器):
numactl --cpunodebind=0 --membind=0 \
taskset -c 8-15 \
redis-benchmark -h 10.0.0.2 -p 6379 \
-t get -d 16 -c 50 -n 1000000000 \
--threads 8 -k 1 -P 16 --csv >> 100w.csv
解释:
-P 16管道 16 条,一次 RTT 打 16 发,降低网络往返;-c 50每线程 50 连接,共 400 连接,刚好把网卡队列打满;- 16 Byte 纯 key,value 打满 PPS;
- 两台机器 10 GbE 直连,交换机无阻塞。
九、结果:100 w QPS 达成
| 指标 | 数值 |
|---|---|
| QPS | 1 004 200 |
| P50 | 0.38 ms |
| P99 | 0.87 ms |
| P999 | 1.2 ms |
| 服务端 CPU | 8 核 97 % |
| 网卡 PPS | 2.01 M |
| 跨 NUMA 内存 | 0 % |
| 上下文切换 | 120 k/s |
| 重传 | 0 |
十、 checklist:30 秒自查脚本
把下面脚本保存为 redis_tune_check.sh,跑完 8 项全绿再压测:
#!/bin/bash
redispid=$(pidof redis-server)
echo ===== NUMA =====
numastat -p $redispid | grep -E "N0|N1" | awk '{if($2>0 && $3>0) print "FAIL: cross-node memory"}'
echo ===== IRQ =====
grep eth0 /proc/interrupts | awk '{if($3>800000) print "WARN:",$0}'
echo ===== CPU =====
top -bn1 | grep redis-server | awk '{if($9<90) print "WARN: CPU<90%"}'
全绿输出 0 行,即可放心打满 100 w。
十一、小结
- redis-benchmark 本身就能压出 100 w,前提是“客户端不抢 CPU、网络不跨 NUMA、中断不撞 Redis”。
- 绑核、NUMA、中断亲和三步缺一不可,顺序不能反:先关 irqbalance → 绑中断 → 绑 Redis → 绑客户端。
- 所有参数里收益最大是
numactl --membind,一步提 18 %;其次是 RPS 亲和,再提 7 %;其余内核参数抠出最后 5 %。 - 生产环境别直接关 AOF,压测才关;上线后把主线程绑到 8-15,bio 线程绑到 0-7,RDB/AOF 落盘再单独绑核,可让抖动 < 2 ms。
下一篇《22. 100 w 之后:多 IO 线程、DPDK、内核 bypass,Redis 能否冲击 200 w?》将用 io_uring + DPDK-redis 把内核协议栈彻底绕过,敬请期待。
更多技术文章见公众号: 大城市小农民
1738

被折叠的 条评论
为什么被折叠?



