11 性能优化与排障
优化从测量开始,排障从分层开始
Section titled “优化从测量开始,排障从分层开始”学习目标 · 面对 Redis 变慢、内存上涨或命中率下降,能提出有顺序的检查和可验证的改进。
11.1 先定义“慢”
Section titled “11.1 先定义“慢””平均值容易掩盖尾部请求。P99 = 99% 的观测请求在该时间内完成,它不是最大值。要区分接口耗时、客户端 Redis 调用耗时与服务端命令执行耗时。一次 300ms 的 GET 可能只在 Redis 执行了 50 微秒,其余时间在网络、排队、连接等待或应用 GC。
| 看到什么 | 先找哪些证据 | 常见下一步 |
|---|---|---|
| 接口慢,Redis 服务端慢日志空 | 调用链、连接等待、网络 RTT、JVM GC | 调整调用次数、连接管理、跨区访问与超时预算 |
| SLOWLOG 出现 HGETALL / 大 Lua | 参数规模、Value 字节、执行时间 | 缩小单次工作量、改结构、分页、缩短脚本 |
| 命中率下降且 evicted_keys 增长 | 内存、Key 增长、TTL、淘汰策略 | 容量和模型优化,识别低价值缓存 |
| 大量超时和数据库负载飙升 | Redis 可用性、回源并发与实例数 | 先限流降级保护 DB,再恢复 Redis |
| 单分片很忙,其余空闲 | 槽分布、热门 Key、hash tag | 处理热点或调整分片分布 |
11.2 常用的第一组诊断命令
Section titled “11.2 常用的第一组诊断命令”这些诊断仍有成本,应按权限和实例负载使用。SLOWLOG 时间单位为微秒,主要记录命令执行,排除与客户端通信的 I/O 等部分;没有慢日志不证明端到端没有问题。默认阈值可能不适合你的观察目标,但修改配置应走正常变更流程。
| 指标 | 正确解读 |
|---|---|
| keyspace_hits / keyspace_misses | Redis 查 Key 的累计命中/未命中;选同一时间窗口求增量再计算比例 |
| evicted_keys / expired_keys | 因内存淘汰 / 到期清理;上涨原因不同 |
| used_memory / used_memory_rss | Redis 分配器使用量 / 进程驻留内存;两者差值要结合碎片、缓冲和系统判断 |
| connected_clients / blocked_clients | 连接数 / 阻塞式命令的客户端数;blocked 不等于服务器彻底卡住 |
| rejected_connections | 达到连接等限制而被拒绝,需要结合配置与趋势 |
| commandstats | 命令调用量和耗时概况;平均耗时不能替代尾延迟 |
命中率用 Δhits / (Δhits + Δmisses);分母为 0 时不要报告 100%。Redis 统计可能包含非业务缓存命令,应用也应埋点区分“命中正常值、命中空值、回源、读取失败、写缓存失败”。
11.3 扫描与删除:控制每次操作的工作量
Section titled “11.3 扫描与删除:控制每次操作的工作量”SCAN 首次传 0,以后把上次返回的游标传回,直到游标为 0 才结束。COUNT 是工作量提示,不保证每次恰好返回 100 条;一次可能返回空列表但游标非 0,也可能重复,遍历期间数据还会变化。Cluster 需要按节点遍历;一次 SCAN 不自动扫全局。
生产避免 KEYS 全量扫描和 HGETALL 大集合。定位一个明确属于自己的大 Key 后,可考虑 UNLINK 让值的主要释放工作异步进行,而不是同步 DEL 一个巨大集合;仍要评估后台释放压力和数据归属,不把整个实例当成练习场。
11.4 批量操作:先减少次数,再调整批大小
Section titled “11.4 批量操作:先减少次数,再调整批大小”查 100 个独立 String,先考虑受控大小的 MGET;复杂的不同命令可以 Pipeline。一个简化模型:串行时间约 N × (RTT + 每条服务时间),批量后约 批次数 × RTT + 总服务时间 + 传输成本。这说明收益来源,不意味着 Pipeline 把 100 条命令变成一条命令。
不要无限堆批量数据:响应缓冲占内存,超大批次也会增加尾延迟。用 100、500 等小规模做自己的测试,逐渐调整;在 Cluster 中按槽/节点和客户端支持处理。结果部分失败或调用超时时可能已有部分命令执行,重试仍要考虑幂等。
11.5 容量、超时与压测:三个常用计算
Section titled “11.5 容量、超时与压测:三个常用计算”容量粗算:100 万个 Key,若采样得到每个 Key 含结构开销约 1.5KiB,则数据集约 1.43GiB。再加复制、客户端缓冲、碎片、增长和持久化峰值余量。副本也要相应内存;maxmemory 不是容器总内存上限。采样不同类型、长度、元素数,不能只测最小对象。
超时预算:接口目标 200ms,却配置每次 Redis 超时 2 秒并重试 3 次,预算已经失效。连接超时、命令超时、重试次数和后续 DB 查询要合起来算。读请求可有界重试;INCR、扣减等写操作超时要先面对“结果未知”,不要盲重试。
优化验证:保持数据量、Value 大小、并发、网络位置和预热方式一致,比较改前/改后的吞吐、P95/P99、错误率、CPU、内存、网络与回源量。下面只用于专用学习实例,禁止直接对共享生产实例压测。
两次分别不加 Pipeline 与每批 16 条。内置压测工具并不等于应用性能:实际 JSON、对象创建、DB 回源和热点分布都没有完整覆盖。它可能创建自己的测试 Key,因此只在专用实例运行。
- “所有优化先加连接池”:Lettuce 可复用连接,多开连接不一定更快;阻塞操作与事务连接需求要分开看。
- “只调大 timeout”:可能把更长的等待堆到应用线程里;先确定瓶颈与业务预算。
- “删除缓存马上解决内存”:如果热点重新回填,还可能引起回源风暴。
采集两次 INFO stats,手算期间命中率;用 SCAN 找自己的学习 Key。比较一个小 Hash 和一个较长 JSON 的 MEMORY USAGE,再解释为什么编码和数据量都影响容量。
自测与答案 · GET 在应用里花了 200ms,SLOWLOG 没记录,接下来查什么?
检查慢日志阈值和采样窗口,同时查客户端连接/请求队列、网络往返、重试、线程阻塞与 GC。SLOWLOG 不包含端到端所有时间。先定位耗时在哪一层,再决定是批量请求、减小 Value、修复热点,还是调整客户端和网络;不能直接推出“Redis 必须扩容”。