Redis 为何 CPU 使用率过高?调试与优化技巧
调查 Redis(关键的内存数据存储)中突然出现的高 CPU 使用率。本指南详细介绍了如何使用 `SLOWLOG` 和 `INFO` 命令调试负载,以定位低效操作(如 `KEYS *` 或大键删除)。学习实用的优化技术,包括切换到异步 `UNLINK`、利用管道化以及调整持久化设置,从而立即降低服务器负载并恢复最佳的 Redis 性能。
Redis 为何 CPU 使用率过高?调试与优化技巧
Redis CPU 使用率高通常意味着以下三种情况之一:Redis 在其主执行路径上执行了过多的命令工作,后台工作(如持久化)增加了压力,或者客户端以 Redis 无法高效处理的模式发送流量。修复方法取决于具体是哪种情况。
除非服务已经濒临崩溃,否则不要一开始就重启 Redis。重启可能会清除症状并抹掉证据。首先捕获命令延迟、命令组合、客户端数量、持久化状态和主机 CPU。这些信息会告诉你问题出在:糟糕的命令、糟糕的流量模式、过载的单核,还是嘈杂的主机。
理解 Redis 架构与 CPU 负载
Redis 通常被描述为单线程的,这对于命令执行来说基本正确,但现代 Redis 也可以使用后台线程和可选的 I/O 线程。实际要点仍然相同:执行时间过长的命令会延迟其他客户端,并且即使机器在其他地方有空闲 CPU,一个饱和的核心也足以产生可见的延迟。
影响 Redis CPU 负载的关键因素
常见原因包括:昂贵的命令、大值、Lua 脚本、每次往返发送太多小命令、频繁的连接建立与断开、持久化活动,以及迫使内核比 Redis 预期更努力工作的内存压力。
调试高 CPU 使用率
在优化之前,必须准确识别负载来源。监控工具和内置的 Redis 命令对于诊断至关重要。
1. 使用 INFO 和 LATENCY 命令
INFO 命令提供服务器状态的快照。重点关注 CPU 部分和命令统计信息。
redis-cli INFO cpu
关注变化率,而不仅仅是绝对值。used_cpu_user 快速增加通常指向命令处理。used_cpu_sys 快速增加可能指向内核工作,例如网络、内存管理或磁盘相关活动。
延迟工具显示 Redis 观察到的事件类别:
redis-cli LATENCY LATEST
redis-cli LATENCY DOCTOR
2. 使用 SLOWLOG 识别慢命令
Redis 慢日志记录超过指定执行时间的命令。这是查找性能不佳操作的最直接工具。
Redis 慢日志记录执行时间超过阈值的命令。它不包括网络时间或在客户端池中等待的时间,因此最好与应用程序延迟指标一起使用。
配置示例:
slowlog-log-slower-than 1000
slowlog-max-len 1024
检索日志:
redis-cli SLOWLOG GET 10
检查命令名称、键名称和持续时间。如果 KEYS、大型 HGETALL、巨大的 SMEMBERS、广泛的排序集范围或 Lua 脚本在日志中占主导地位,那么 CPU 问题很可能是由应用程序驱动的。
3. 监控网络和客户端活动
在事件期间,MONITOR 很诱人,但在繁忙的服务器上它非常昂贵。优先使用 INFO commandstats、INFO clients、慢日志、客户端库指标,以及如果可用的话,从副本进行采样。
有用的命令:
redis-cli INFO commandstats
redis-cli INFO clients
redis-cli CLIENT LIST
如果部署后命令量翻倍,你可能会在 cmdstat_get、cmdstat_hgetall 或类似的计数器中看到。如果客户端不断连接和断开,请在调整 Redis 之前先修复连接池。
常见原因与优化策略
一旦确定了有问题的命令或进程,应用有针对性的优化技术。
1. 消除阻塞命令
最快的改进通常来自移除那些迫使 Redis 遍历巨大键空间或序列化巨大值的命令。
| 低效命令 | 导致高 CPU 的原因 | 优化/替代方案 |
|---|---|---|
KEYS * |
扫描整个键空间。O(N)。 | 使用 SCAN 迭代或重构数据访问。 |
FLUSHALL / FLUSHDB |
除非使用异步模式,否则删除每个键。 | 使用谨慎的范围删除、UNLINK,或仅在适当时使用异步刷新。 |
HGETALL、SMEMBERS(针对非常大的集合) |
将整个结构检索到内存中并序列化。 | 使用 HSCAN、SSCAN,或将大型结构分解为更小的键。 |
对于非常大的键,使用 UNLINK 代替 DEL。DEL 同步释放内存。UNLINK 从键空间中移除键并异步释放内存,这通常可以减少大删除期间的可见延迟。
# 代替 DEL large_key
UNLINK large_key
2. 优化持久化(RDB 和 AOF)
RDB 快照和 AOF 重写使用后台子进程,但仍然可能通过 fork 成本、写时复制内存、磁盘带宽和 CPU 争用来影响父进程。
- RDB 快照: 如果你频繁保存(例如,每分钟一次),重复的
fork()调用会导致反复的 CPU 峰值。减少自动保存的频率。 - AOF 重写: AOF 重写(
BGREWRITEAOF)也消耗大量资源。Redis 尝试通过执行最少的 I/O 来优化此过程,但在此过程中 CPU 使用率会上升。
如果持久化与 CPU 峰值时间点吻合,请检查 INFO persistence 和主机磁盘指标。你可以降低 RDB 频率,将繁重的备份安排在流量低谷期,保留更多内存余量,或改进存储。暂停持久化可以减少负载,但也会增加数据丢失风险,因此这应该是一个深思熟虑的操作决策。
3. 处理内存碎片和交换
虽然内存问题通常与高内存使用率相关,但严重的内存碎片化,或者更糟的是,操作系统开始将 Redis 数据交换到磁盘(抖动),会急剧增加 CPU 使用率,因为内核在努力管理内存。
- 检查交换: 使用操作系统工具(
vmstat、top)检查系统是否正在主动交换属于 Redis 进程的内存页。 - 内存碎片比率: 检查
INFO memory中的mem_fragmentation_ratio。高比率表明分配器行为可能浪费了内存,但请通过 RSS、数据集大小和主机内存指标进行确认。
如果发生交换,请减少数据集,降低 maxmemory,将工作移出主机,或增加内存。Redis 的设计初衷并非在其热数据集被分页到磁盘时仍能良好运行。
4. 网络优化与管道化
如果 CPU 负载与大量小命令相关,问题可能在于命令开销和网络波动,而不是某个明显缓慢的命令。
管道化允许客户端发送多个命令而无需在每个命令后等待响应。它减少了往返次数,并可以提高批量写入或读取的吞吐量。保持管道批次有界;包含数千个繁重命令的管道可能会产生自身的延迟峰值。
持续性能的最佳实践
为防止未来的 CPU 峰值,请采用以下架构和配置最佳实践:
- 对可能很大的键使用
UNLINK。 - 用
SCAN替换KEYS,并用基于游标的读取替换完整集合读取。 - 在部署后跟踪
INFO commandstats,这样新的命令模式就不会让你措手不及。 - 根据实际的磁盘和内存余量调整持久化设置。
- 如果单个 Redis 实例在命令修复后仍然饱和,请使用 Redis 集群、客户端分片、单独的缓存/会话实例,或具有更好单核性能的更大实例来拆分工作负载。
快速事件检查清单
在峰值期间,运行:
redis-cli INFO cpu
redis-cli INFO commandstats
redis-cli INFO clients
redis-cli INFO memory
redis-cli INFO persistence
redis-cli SLOWLOG GET 20
redis-cli LATENCY LATEST
然后将这些结果与应用程序部署、定时任务、流量变化、持久化事件和主机指标进行对比。Redis CPU 使用率高通常是可以修复的,但修复方法是具体的:移除昂贵的命令,批量处理健谈的客户端,停止连接波动,给持久化留出工作空间,或者在单个实例确实达到极限时拆分工作负载。