RedisNotes

第 20 章:性能调优

zjc 于 2026-01-20 发布

这是《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

压测建议

  1. 使用与生产相同的数据大小与命令;
  2. 分离压测客户端与 Redis;
  3. 分别测 GET/SET/HGETALL/ZADD/Lua;
  4. 改一个参数测一轮;
  5. 记录 CPU、内存、网卡、磁盘;
  6. 不要在业务高峰压生产实例。

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

推荐工具:

治理:

  1. Hash 按字段分片;
  2. ZSet 按时间/范围分片;
  3. List 使用 LTRIM 控制长度;
  4. Stream 使用 XTRIM
  5. 大文本放对象存储,Redis 存 URL;
  6. 读取必须分页;
  7. 删除使用 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 快速执行

建议:

20.8 客户端调优

连接池大小:按 QPS * Redis平均耗时估算
命令超时:通常 100ms-1s
连接超时:100ms-1s
重试:读幂等操作 1 次,写操作业务幂等后再重试
序列化:避免超大大对象
连接名:便于 CLIENT LIST 定位

客户端侧常见问题:

20.9 内存调优

INFO memory

关注:

used_memory
maxmemory
mem_fragmentation_ratio
evicted_keys
lazyfree_pending_objects

动作:

  1. 控制实例内存规模(常见单实例 10-30GB 内);
  2. 拆大 key;
  3. 缓存加 TTL;
  4. 调整 listpack 阈值;
  5. 清理僵尸 key;
  6. 碎片率高时评估 active defrag;
  7. 不把 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

原则:

20.12 IO 多线程调优

适用:网络读写和协议解析成为瓶颈,CPU 核心充足。

io-threads 4
io-threads-do-reads yes

不适用:

验证方式:

开启前: 记录 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 最常见的反模式。

本章小结

思考题

  1. Redis P99 高但 CPU 低,可能的问题在哪里?
  2. 为什么大 key 会同时造成 Redis 延迟、客户端超时和主从复制压力?
  3. 如何为 20GB 数据量的 Redis 实例规划机器内存?列出所有预留项。