Redis的阻塞
Redis的阻塞问题主要分为内在原因和外在原因两大类,以下从这两个维度展开分析:
一、内在原因
1. 不合理使用API或数据结构
-
Redis 慢查询
-
Redis 慢查询的界定
- 定义:Redis 慢查询指命令执行时间超过预设阈值(默认 10ms)的操作,仅统计命令执行阶段耗时
-
slowlog-log-slower-than
:阈值(单位微秒),默认 10000(10ms),建议调整为 1000(1ms) -
slowlog-max-len
:慢查询日志队列长度,默认 128,建议提升至 1000+ 避免日志覆盖。
-
典型慢查询命令及风险场景
-
高时间复杂度命令(O(N)及以上)
-
全量遍历类:
-
KEYS *
:遍历所有键(复杂度 O(n)),可能引发长时间阻塞。 -
SMEMBERS
:获取集合全部元素(O(n)),百万级数据时耗时显著。
-
-
聚合计算类:
-
SORT
:排序操作(O(n log n)),大列表排序时性能骤降。 -
ZUNIONSTORE
/SUNION
:多集合交并操作(O(n)),数据量越大耗时越长。
-
-
大范围查询类:
-
HGETALL
:获取哈希所有字段(O(n)),10MB+ 的 Hash 操作延迟显著。 -
LRANGE 0 -1
:获取列表全部元素(O(n)),可能占用主线程数秒。
-
-
-
- BigKey 操作
-
定义:存储大量数据的 Key(如 10MB Hash、百万元素 List)
-
典型风险命令:
-
DEL
:删除 BigKey 时触发内存回收阻塞(单线程模型下耗时极长)。 -
GET
/SET
:序列化/反序列化大 Value 时耗时增加(如 AOF 重写阶段)。 -
EXPIRE
:对大 Key 设置过期时间可能引发后续淘汰阻塞。
-
-
非显式高耗时操作
- Pipeline 不当使用:一次性发送过多命令导致单次执行时间超过阈值。
- Lua 脚本阻塞:执行复杂脚本(如循环遍历大数据)时未分批次处理。
- 事务(MULTI/EXEC) :事务中包含多个高耗时命令时整体被视为慢查询。
-
-
排查与优化建议
-
查看日志:
SLOWLOG GET [n] # 获取最近 n 条慢查询记录(含命令、耗时、客户端 IP) SLOWLOG LEN # 统计当前日志数量
-
日志字段解析:
-
id
:唯一标识 -
timestamp
:执行时间戳 -
duration
:耗时(微秒) -
command
:完整命令及参数。
-
-
优化策略
-
拆分 BigKey:
- 将大 Hash 拆分为多个子 Key,通过分片降低单次操作负载。
- 使用
LPOP
/RPOP
分批次删除大 List,避免DEL
阻塞。
-
配置调优:
- 动态调整阈值:
CONFIG SET slowlog-log-slower-than 1000
。 - 禁用高风险命令:通过
rename-command
屏蔽KEYS
。
- 动态调整阈值:
-
替换高复杂度命令:
- 用
SCAN
替代KEYS
,HSCAN
替代HGETALL
。 - 对排序需求改用
ZRANGE
或客户端计算。
- 用
-
-
监控与预警
-
工具:
-
redis-cli --bigkeys
:扫描内存中的 BigKey。 - Prometheus + Grafana:可视化监控慢查询频率及耗时分布。
-
-
告警规则:
- 单命令耗时 > 5ms 时触发告警。
- 慢查询日志长度连续增长时排查潜在性能瓶颈。
-
-
-
总结:慢查询命令速查表
命令类型 典型命令 风险场景 优化方案 全量遍历 KEYS *
、SMEMBERS
键数量多、集合元素量大 改用 SCAN
、分片存储聚合计算 SORT
、ZUNIONSTORE
数据量大、多集合操作 客户端计算、预聚合 大范围查询 HGETALL
、LRANGE 0 -1
Hash/List 体积大 分批次获取、压缩存储格式 BigKey 操作 DEL
、GET
(大 Value)内存回收、序列化开销 渐进式删除、拆分 Key
-
2. CPU饱和
-
CPU饱和的定义 :
- CPU饱和指Redis单核CPU使用率长期接近或达到100%的临界状态。由于Redis采用单线程模型,所有请求由主线程顺序处理,一旦CPU满载会导致命令队列积压、响应延迟暴增,甚至引发服务雪崩。这种现象在高并发或复杂操作场景下尤为危险
-
判断并发量是否达极限
-
redis-cli --stat
命令分析 每秒输出一次统计信息,重点关注requests
字段(即OPS,每秒操作数)redis-cli --stat # 典型CPU饱和时的输出特征: # 1. 每秒处理请求量持续在5万+(普通服务器极限约8-10万OPS) # 2. 客户端连接数(clients)持续高位且无明显波动
-
top
命令监控 直接观察Redis进程的CPU使用率top -p $(pgrep redis-server) # 当CPU使用率≥95%且持续不降时,可判定为饱和状态
-
INFO commandstats
分析命令耗时 观察高频命令的usec_per_call
(单次调用微秒数cmdstat_hset:calls=198757512,usec=27021957243,usec_per_call=135.95 # 正常O(1)命令应≤10微秒,若值异常高(如135μs)可能存在配置或数据结构问题
-
-
问题根源分析
-
高算法复杂度命令
-
过度内存优化
- 修改
hash-max-ziplist-entries
等参数过度压缩数据结构,导致操作复杂度从O(1)退化为O(n)
- 修改
-
持久化操作竞争
- Fork阻塞
- AOF刷盘
-
连接数过载 : 短连接频繁创建或
maxclients
设置过低,导致TCP握手/断连消耗CPU资源
-
3. 持久化阻塞
-
RDB生成:
BGSAVE
触发fork操作时,若内存过大(如10GB),复制页表可能导致主线程暂停(典型耗时约20ms/GB)。 -
AOF重写:
BGREWRITEAOF
期间,主线程需将缓冲区数据追加到新AOF文件,可能因磁盘压力大而阻塞。-
优化方案:
- 调整RDB触发频率,避免高峰期执行。
- 使用
appendfsync everysec
替代always
,降低磁盘I/O压力。 - 关闭透明大页(THP),避免内存页复制效率降低。
-
二、外在原因
1. CPU竞争
-
Redis部署在多核服务器时,若与其他进程竞争CPU资源,或父子进程(如RDB/AOF重写)绑定同一核心,会导致性能下降。
-
优化方案:
-
将Redis部署在专用服务器,避免资源争抢。
-
调整进程绑定策略(如父进程与子进程绑定不同核心)。
-
错误的现象 :
flowchart TDsubgraph CPU核心1A[Redis主线程\n(父进程)]B[RDB/AOF子进程]endA -->|fork| BA -->|处理客户端请求\n(高优先级)| C[CPU时间片]B -->|生成快照/重写AOF\n(低优先级)| CC -->|资源抢占| D[响应延迟增加]
-
正确的现象
flowchart TDsubgraph 物理CPUsubgraph 核心0-3A[Redis主线程\n(绑定核心0-3)]endsubgraph 核心4-7B[RDB/AOF子进程\n(绑定核心4-7)]endendA -->|fork| BA -->|独占核心0-3| C[高效处理请求]B -->|独占核心4-7| D[无干扰持久化]
-
-
-
2. 内存交换(Swap)
-
内存交换是操作系统的内存管理机制,当物理内存不足时,系统会将部分内存中的冷数据(长时间未被访问)移动到磁盘的 Swap 分区,以腾出内存空间给其他进程使用。
对 Redis 而言,数据原本应完全驻留内存以实现高性能(微秒级响应)。若发生 Swap,访问被换出的数据需经历磁盘 I/O(毫秒级),响应延迟骤增 5~10 万倍。 -
内存交换流程图
flowchart TDsubgraph 物理内存A[Redis 热点数据\n(高频访问)]B[Redis 冷数据\n(长时间未访问)]C[其他进程占用的内存]endsubgraph 磁盘Swap分区D[被换出的冷数据]endA -->|持续活跃| E[正常响应(微秒级)]B -->|内存不足触发交换| F[Swap Out操作]F -->|数据写入磁盘| DC -->|占用内存增加| FD -->|再次被访问| G[Swap In操作]G -->|数据加载回内存| H[高延迟响应(毫秒级)]
-
流程关键点
-
触发条件(内存不足)
- Redis 自身内存超限(如未设置
maxmemory
) - 同一服务器运行其他内存密集型进程(如大数据处理、文件 I/O)
- Redis 自身内存超限(如未设置
-
Swap Out
操作系统将冷数据从内存迁移到 Swap 分区,释放物理内存空间。
-
Swap In
Redis 需访问已换出的数据时,触发磁盘读取和数据回迁,导致延迟暴增。
-
性能影响
单次 Swap 操作可能引入数毫秒延迟,若高频触发则整体吞吐量断崖式下跌
-
-
-
内存交换的核心原因
-
Redis 内存超限
- 未配置
maxmemory
:Redis 默认无内存限制,可能无限增长直至触发 Swap。 - BigKey 或内存碎片:大对象(如 10MB Hash)或碎片化内存占用超出预期
- 未配置
-
操作系统资源竞争
- 多进程共存:同一服务器运行 MySQL、Hadoop 等内存密集型服务,挤占 Redis 可用内存。
- Swap 配置不合理:Linux 默认
vm.swappiness=60
,内存压力大时激进换出数据。
-
硬件限制
- 物理内存不足:服务器内存容量无法支撑 Redis 数据集规模
- 磁盘性能差:机械硬盘的 Swap 操作延迟远高于 SSD(如 10ms vs 0.1ms)。
-
-
监控与诊断工具
-
检查 Swap 使用量
# 查看 Redis 进程 Swap 情况 redis-cli info | grep process_id # 获取 PID cat /proc/<PID>/smaps | grep -i swap
-
性能分析工具
-
redis-cli --latency
:检测 Redis 响应延迟波动。 -
vmstat
:监控系统级 Swap I/O 频率(si/so
列)
-
-
3. 网络问题
-
连接数过多:短连接频繁创建或
maxclients
设置过低,导致TCP连接处理消耗CPU资源。 -
带宽不足:高吞吐场景下网络打满,或使用
MONITOR
命令记录所有请求。-
优化方案:
- 使用连接池管理长连接,设置
timeout
自动关闭空闲连接。 - 禁用
MONITOR
,通过Pipeline
批量请求减少网络往返。
- 使用连接池管理长连接,设置
-
三、总结与优化策略
阻塞类型 | 关键表现 | 优先级 | 解决方案 |
---|---|---|---|
大Key操作 | 单命令执行时间过长 | 高 | 拆分Key、改用分片或压缩结构 |
持久化fork延迟 | latest_fork_usec 值高 | 高 | 降低RDB频率、优化内存页管理 |
CPU竞争 | 服务器整体CPU饱和 | 中 | 隔离部署、绑定CPU核心 |
内存交换 | used_memory_rss 异常高 | 紧急 | 禁用Swap、增加物理内存 |
综合建议:
- 监控工具:使用
SLOWLOG
、INFO commandstats
分析慢查询,redis-cli --bigkeys
扫描大Key。 - 架构设计:采用读写分离、集群分片(Redis Cluster)分散负载。
- 配置调优:调整
maxmemory
、repl-backlog-size
,优化淘汰策略(如volatile-lru
)。
通过上述措施,可显著降低Redis阻塞风险,提升系统稳定性。