这是《Redis 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Redis 性能问题的常见真相是:不是 Redis 不快,而是用法破坏了它的假设。本章给出一套从压测、定位到落地的调优方法。
20.1 先定义目标
调优前必须量化:
目标 QPS 是多少?
P99 / P999 延迟要求是多少?
读写比例是多少?
value 大小分布是多少?
允许丢失吗?
是否允许强依赖降级?
Redis 常见目标:
缓存:P99 < 5ms,命中率 > 95%
会话:P99 < 10ms,可用性优先
锁/库存:正确性优先,P99 < 20ms
大对象读取:必须拆分,不能直接进 Redis
20.2 基准测试
redis-benchmark
redis-benchmark \
-h 127.0.0.1 \
-p 6379 \
-a redis123 \
-n 1000000 \
-c 200 \
-d 1024 \
-t get,set \
-P 16 \
--threads 4
参数:
| 参数 | 含义 |
|---|---|
-n |
总请求数 |
-c |
并发连接 |
-d |
value 大小 |
-t |
测试命令 |
-P |
pipeline 数 |
--threads |
客户端线程 |
输出关注:
throughput
average latency
percentile latency
压测建议
- 使用与生产相同的数据大小与命令;
- 分离压测客户端与 Redis;
- 分别测 GET/SET/HGETALL/ZADD/Lua;
- 改一个参数测一轮;
- 记录 CPU、内存、网卡、磁盘;
- 不要在业务高峰压生产实例。
20.3 性能问题分层
客户端
连接池不足 / GC / 序列化 / 超时配置
网络
RTT / 带宽 / 重传 / 连接重建
Redis
慢命令 / 大 key / 热点 / 内存淘汰 / fork / AOF
系统
CPU limit / swap / NUMA / 磁盘 / 中断
架构
单点热点 / 强依赖 / 缓存 miss / 数据倾斜
不要看到延迟就改 io-threads。先看慢查询和大 key。
20.4 慢命令治理
SLOWLOG GET 50
常见慢命令:
| 命令 | 原因 | 替代 |
|---|---|---|
KEYS pattern |
全量扫描 | SCAN |
HGETALL big |
大响应 | HMGET / HSCAN |
SMEMBERS big |
大响应 | SSCAN / SRANDMEMBER n |
LRANGE 0 -1 |
大列表 | 分页 |
ZRANGE 0 -1 |
大 ZSet | 分页 |
DEL big |
同步释放 | UNLINK |
SINTER/SDIFF big |
集合计算 | 预计算/拆分 |
EXPIRE 大量 key 同刻 |
删除尖刺 | TTL 随机化 |
20.5 大 key 治理
发现:
MEMORY USAGE key
MEMORY DOCTOR
SCAN cursor MATCH pattern COUNT 100
推荐工具:
redis-cli --bigkeys:低峰周期扫描;- RDB 离线分析工具;
- 云厂商大 key 分析;
- 业务埋点上报 value 大小。
治理:
- Hash 按字段分片;
- ZSet 按时间/范围分片;
- List 使用
LTRIM控制长度; - Stream 使用
XTRIM; - 大文本放对象存储,Redis 存 URL;
- 读取必须分页;
- 删除使用
UNLINK。
20.6 热点 key 治理
现象:单 key QPS 极高,单节点 CPU/网卡打满,其他节点空闲。
1. 本地缓存
请求 -> JVM 本地缓存 -> Redis -> DB
适合不频繁变化的配置、商品基础信息。
2. key 打散
hot:key:{0..N}
随机读一个副本,写时更新全部或异步失效
3. 读写分离
读请求分散到多个 replica;注意复制延迟。
4. Cluster + 前置路由
热点业务单独拆集群或节点,避免影响其他 key。
5. 业务削峰
前端限流、合并请求、异步刷新。
20.7 Pipeline 与批量
逐条请求:
100 命令 * 1ms RTT = 100ms 网络
Pipeline:
1 次批量传输 + Redis 快速执行
建议:
- 每批 100-1000 条;
- 控制总字节数;
- 大 key 不入同一批;
- 异常时仍可分批重试;
- 不要在高峰一次性导入百万数据。
20.8 客户端调优
连接池大小:按 QPS * Redis平均耗时估算
命令超时:通常 100ms-1s
连接超时:100ms-1s
重试:读幂等操作 1 次,写操作业务幂等后再重试
序列化:避免超大大对象
连接名:便于 CLIENT LIST 定位
客户端侧常见问题:
- 池太小导致等待;
- 池太大导致 Redis 连接数高;
- 每次请求新建连接;
- 反序列化大 JSON;
- 应用 GC 停顿被误判为 Redis 慢;
- 重试叠加造成雪崩。
20.9 内存调优
INFO memory
关注:
used_memory
maxmemory
mem_fragmentation_ratio
evicted_keys
lazyfree_pending_objects
动作:
- 控制实例内存规模(常见单实例 10-30GB 内);
- 拆大 key;
- 缓存加 TTL;
- 调整 listpack 阈值;
- 清理僵尸 key;
- 碎片率高时评估 active defrag;
- 不把 Redis 当对象存储。
20.10 系统层调优
| 项 | 建议 |
|---|---|
| CPU | 独占或设置合理 limit,避免与重负载混部 |
| 内存 | 禁用 swap,预留 fork/COW |
| 网卡 | 关注带宽与重传,大 value 场景重点 |
| 磁盘 | AOF/RDB 独立盘或高性能盘 |
| NUMA | 绑定节点减少跨节点访问 |
| 文件句柄 | ulimit -n 足够 |
| overcommit | COW 场景合理配置 |
Linux 示例:
sysctl vm.overcommit_memory=1
sysctl vm.swappiness=1
云容器特别注意:CPU limit 被打满时,延迟尖刺很像 Redis 慢;内存 limit 未预留 COW 时,BGSAVE 可能 OOM。
20.11 AOF/RDB 调优
appendfsync everysec
auto-aof-rewrite-min-size 2gb
no-appendfsync-on-rewrite no
原则:
- 磁盘慢会传导为写延迟;
- 大实例 fork 时间变长;
- 多实例错峰 rewrite;
- 备份在从节点执行;
- 观察
latest_fork_usec和aof_delayed_fsync。
20.12 IO 多线程调优
适用:网络读写和协议解析成为瓶颈,CPU 核心充足。
io-threads 4
io-threads-do-reads yes
不适用:
- 慢命令为主;
- 大 key 为主;
- CPU 已经不足;
- 容器只有 1-2 核。
验证方式:
开启前: 记录 QPS/P99/CPU
开启后: 相同压测对比
如果吞吐无提升或 P99 变差,回退
20.13 容量规划
估算:
数据内存 =
key数量 * 平均key大小
+ value数量 * 平均对象头
+ 集合索引/指针
+ TTL元数据
预留 =
业务增长
+ 复制缓冲
+ 客户端输出缓冲
+ fork/COW
+ 碎片
经验:
纯缓存:maxmemory 设置机器内存 70%-80%
持久化/状态实例:预留 30%-40% 给 COW/缓冲
Cluster:每主节点保持 10-30GB,过大不利迁移和故障恢复
20.14 一个调优案例
现象:
接口 P99 从 8ms 涨到 300ms
Redis CPU 85%
SLOWLOG 大量 HGETALL,耗时 20-80ms
定位:
某 Hash 有 12 万 field
接口每次 HGETALL 后本地取 3 个字段
治理:
1. 接口改为 HMGET 3 个必要字段
2. Hash 按用户维度拆分
3. 大 Hash UNLINK 下线
4. 上线大 key 扫描和发布检查
结果:
Redis CPU 降到 35%
P99 回到 6ms
这类“读全量再丢掉大部分”的模式,是 Redis 最常见的反模式。
本章小结
- 先定 QPS/P99 目标,再做对比压测;
- 优先治理慢命令、大 key、热点 key 和客户端;
- Pipeline 能显著降低 RTT,但要控制批次;
- 内存容量必须预留复制缓冲、fork/COW 和碎片;
io-threads只解决网络 IO 瓶颈,不解决慢命令;- 系统资源、持久化与容器 limit 同样关键。
思考题
- Redis P99 高但 CPU 低,可能的问题在哪里?
- 为什么大 key 会同时造成 Redis 延迟、客户端超时和主从复制压力?
- 如何为 20GB 数据量的 Redis 实例规划机器内存?列出所有预留项。