一、Redis 阻塞的基本概念与危害
1.1 什么是 Redis 阻塞?
Redis 作为一款高性能的内存数据库,其核心采用单线程模型处理网络 I/O 和键值对操作(从 Redis 6.0 开始引入了多线程 I/O,但核心命令处理仍是单线程)。这种设计有以下特点:
- 单线程处理机制:所有客户端请求都会进入一个队列,由单个主线程顺序处理
- 高效的事件循环:基于 epoll/kqueue 等 I/O 多路复用技术,能同时处理大量连接
- 微秒级响应:正常情况下,简单命令(如 GET/SET)执行时间在 10-100 微秒之间
Redis 阻塞的具体表现是:
- 某个命令执行时间异常(如超过 1 毫秒)
- 事件循环被长时间占用
- 后续请求必须等待当前命令完成
- 客户端观察到响应时间显著增加
典型阻塞场景示例:
- 执行复杂度为 O(N) 的命令(如 KEYS *)操作百万级键
- 大 value(MB 级别)的读写操作
- 持久化时 fork 操作导致的内存拷贝
- 慢查询日志中记录的执行时间过长的命令
1.2 Redis 阻塞的危害
1.2.1 客户端请求堆积
阻塞会导致:
- 请求队列积压(默认最大队列长度 10,000)
- 新连接被拒绝(出现 "max number of clients reached" 错误)
- 客户端出现连接超时(常见报错:Connection timed out)
- 重试机制导致雪崩效应(客户端不断重试加重阻塞)
实际案例: 某电商网站在大促期间,因 Redis 执行了一个耗时 2 秒的 LRANGE 操作,导致 10 秒内积压了 8,000 个请求,最终触发了连接拒绝。
1.2.2 业务响应延迟
影响路径: Redis 阻塞 → 缓存查询延迟 → 业务接口超时 → 用户体验下降
典型表现:
- API 响应时间从 50ms 突增到 2s+
- 前端页面加载出现明显卡顿
- 超时重试导致系统负载进一步升高
1.2.3 服务级联故障
连锁反应过程:
- 服务 A 因 Redis 阻塞变慢
- 服务 A 的线程池被占满
- 调用服务 A 的服务 B 也开始阻塞
- 最终整个调用链瘫痪
特别危险的场景:
- 分布式锁服务阻塞导致锁无法释放
- 秒杀系统中的库存服务阻塞
- 支付系统中的幂等控制失效
1.2.4 数据一致性风险
主要风险点:
主从同步问题:
- 主节点阻塞导致复制积压缓冲区溢出
- 从节点出现全量同步
- 主从数据不一致时间窗口延长
持久化问题:
- AOF 重写期间的阻塞导致写入失败
- RDB 生成时 fork 阻塞导致数据丢失
- 混合持久化模式下出现数据断层
集群问题:
- 节点阻塞导致集群状态误判
- 迁移槽位操作超时
- 故障转移延迟增加
二、Redis 阻塞的常见类型与成因
2.1 内部操作阻塞
2.1.1 慢查询命令阻塞
详细分析: Redis 命令执行时间的差异主要源于其时间复杂度。简单键值操作如 GET/SET 只需常数时间,而集合操作则可能随数据规模线性增长。
实际案例分析:
- 某电商网站在促销活动期间使用 KEYS * 命令查询商品缓存,导致 Redis 阻塞 3 秒,期间丢失大量订单
- 社交平台使用 HGETALL 获取用户资料哈希表,当用户资料字段超过 5000 个时,响应时间从 1ms 骤增至 50ms
优化建议:
- 使用 SCAN 替代 KEYS 实现渐进式遍历
- 对于大型哈希,采用 HSCAN 分批获取
- 将大集合拆分为多个小集合
2.1.2 持久化操作阻塞
RDB 持久化深度解析:
- Fork 过程详细机制:采用 copy-on-write 技术,但 fork 时仍需复制页表
- 内存影响示例:32GB 实例 fork 时间可达 500ms-1s
- 监控指标:可通过
info stats查看 latest_fork_usec
AOF 持久化实战问题:
- always 策略下,SSD 磁盘的写入延迟通常在 1-5ms,HDD 可能达 10-50ms
- everysec 策略在突发流量时可能产生 2MB+的 AOF 缓冲区
- AOF 重写期间的磁盘 I/O 峰值为正常写入的 2-3 倍
2.1.3 主从同步阻塞
全量同步性能数据:
- 10GB RDB 文件在千兆网络下传输需要约 100 秒
- 从节点加载 10GB RDB 耗时通常为 3-5 分钟
- 同步期间主节点内存可能增长 50%(写操作积压)
复制积压缓冲区配置原则:
- 建议大小 = 平均写入速率(MB/s) × 断线重连时间(s) × 2
- 典型配置:100MB-1GB
- 监控指标
repl_backlog_active和repl_backlog_size
2.2 外部因素阻塞
2.2.1 网络问题阻塞
网络延迟场景分析:
- 同机房延迟:0.1-0.3ms
- 跨城延迟:10-50ms
- 跨境延迟:100-300ms
TCP 参数调优建议:
- tcp-backlog = 预计最大连接数 × 1.2
- 启用 TCP keepalive(tcp-keepalive 300)
- 考虑使用 SO_REUSEPORT 特性
2.2.2 操作系统配置问题
内存交换预防方案:
- 设置 vm.swappiness=0
- 监控 /proc/<pid>/smaps 中的 Swap 使用
- 使用 cgroups 限制 Redis 内存
CPU 绑核实践:
- 通过 taskset 绑定 CPU 核心
- 隔离 NUMA 节点(numactl --interleave=all)
- 监控 CPU 窃取时间(steal time)
2.2.3 客户端问题
连接池最佳实践:
- 最大连接数 = QPS / (1000/平均响应时间(ms))
- 空闲连接检测周期建议 30-60 秒
- 连接泄漏检测机制实现方案
大命令处理策略:
- 设置 client-output-buffer-limit
- 实现数据分片(如将 100MB 数据拆分为 1000 个 100KB 的块)
- 使用 pipeline 控制批量操作规模
2.3 资源竞争阻塞
2.3.1 分布式锁优化
Redlock 算法实现细节:
- 时钟漂移补偿机制
- 锁自动续期设计
- 故障转移处理逻辑
锁竞争缓解方案:
- 锁分段(将单个锁拆分为多个)
- 乐观锁替代方案
- 本地缓存+异步更新模式
2.3.2 内存管理进阶
内存分配器对比:
- jemalloc:适合多线程场景,碎片率低
- tcmalloc:分配速度快,但长期运行可能产生碎片
- glibc malloc:简单但性能一般
内存淘汰策略调优:
- volatile-ttl 与热数据保留
- LFU 策略的计数器衰减配置
- 主动淘汰与被动淘汰的平衡点
三、Redis 阻塞问题的排查工具与方法
3.1 Redis 自带的排查工具
Redis 提供了多个内置的命令和配置选项,可以帮助开发者和运维人员实时监控 Redis 实例的运行状态,快速定位和排查可能导致阻塞的各种问题。这些工具是 Redis 性能调优和问题排查的基础设施,掌握它们的使用方法对于维护高性能的 Redis 服务至关重要。
3.1.1 慢查询日志(slowlog)
慢查询日志是 Redis 内置的性能分析工具,专门用于记录执行时间超过预设阈值的命令。这个功能对于发现和优化慢查询命令非常有效,是排查 Redis 阻塞问题的首要工具。
详细配置参数
-
slowlog-log-slower-than:
- 单位:微秒(1秒=1,000,000微秒)
- 默认值:10000(即10毫秒)
- 建议设置:生产环境通常设置为5000-20000微秒(5-20毫秒)
- 特殊说明:设置为0会记录所有命令,设置为-1则完全禁用慢查询日志
-
slowlog-max-len:
- 默认值:128条
- 建议设置:生产环境建议设置为1024或更大
- 存储方式:使用内存存储,不会持久化到磁盘
命令使用详解
-
slowlog get [n]:
- 示例:
slowlog get 5获取最近5条慢查询记录 - 输出格式:每条记录包含6个字段
- 注意事项:获取大量日志可能影响Redis性能
- 示例:
-
slowlog len:
- 使用场景:监控慢查询数量增长趋势
- 典型应用:可以设置监控告警,当日志数量超过阈值时报警
-
slowlog reset:
- 注意事项:清空后无法恢复
- 使用建议:定期清理,但清理前建议先备份
日志字段详细说明
-
唯一ID:
- 特点:单调递增,重启Redis后重置
- 用途:可用于排序和去重
-
执行时间戳:
- 格式:Unix时间戳(秒级)
- 示例:1690000000 表示2023-07-22 16:26:40 UTC
-
执行耗时:
- 单位:微秒
- 判断标准:包括命令排队时间和执行时间
-
命令详情:
- 格式:数组形式
- 安全性:会记录完整命令和参数
- 示例:对于
HGETALL user:1000,记录为1) "HGETALL" 2) "user:1000"
-
客户端信息:
- 格式:IP:端口
- 特殊客户端:如为内部命令则显示特殊标识
-
客户端名称:
- 设置方式:通过
CLIENT SETNAME设置 - 用途:方便识别不同业务客户端
- 设置方式:通过
实际应用场景
-
发现大键操作:
- 示例:频繁出现
HGETALL命令耗时高,可能是在操作大哈希表 - 解决方案:考虑拆分为多个小键或使用
HSCAN
- 示例:频繁出现
-
识别O(N)命令:
- 典型案例:
KEYS *、FLUSHALL、LRANGE 0 -1 - 推荐替代:使用
SCAN系列命令
- 典型案例:
-
监控查询模式变化:
- 方法:定期收集慢日志,分析命令类型分布
- 工具:可以开发脚本自动分析慢日志趋势
3.1.2 info 命令
INFO命令是Redis最全面的监控命令,它提供了Redis实例几乎所有方面的运行信息,是性能分析和问题排查的核心工具。
命令使用方式
基本语法:INFO [section],其中section可选,不指定则返回所有信息
关键信息块详解
1. info server
主要字段:
redis_version:Redis版本号process_id:进程IDuptime_in_seconds:运行时长(秒)uptime_in_days:运行天数hz:内部定时任务频率lru_clock:LRU时钟executable:可执行文件路径config_file:配置文件路径
典型问题诊断:
- 版本过低可能导致已知性能问题
- 运行时间异常短可能是不正常重启
2. info memory
关键指标:
used_memory:Redis分配器分配的内存总量used_memory_human:人类可读格式used_memory_rss:操作系统看到的内存用量used_memory_peak:内存使用峰值used_memory_peak_human:峰值人类可读格式used_memory_lua:Lua引擎内存用量mem_fragmentation_ratio:内存碎片率mem_allocator:内存分配器类型
内存问题诊断:
- 碎片率>1.5:考虑重启或使用
MEMORY PURGE(Redis 4.0+) - RSS远大于used_memory:可能有内存交换
- Lua内存过高:检查Lua脚本
3. info clients
重要指标:
connected_clients:客户端连接数client_longest_output_list:最大输出队列client_biggest_input_buf:最大输入缓冲区blocked_clients:被阻塞的客户端数
连接问题诊断:
- 连接数突增:可能客户端连接泄漏
- 阻塞客户端过多:检查
BLPOP等阻塞命令 - 大输入缓冲区:可能有慢查询
4. info persistence
RDB相关:
rdb_last_save_time:上次保存时间戳rdb_last_bgsave_status:上次后台保存状态rdb_last_bgsave_time_sec:上次保存耗时rdb_current_bgsave_time_sec:当前保存耗时
AOF相关:
aof_enabled:AOF是否启用aof_rewrite_in_progress:是否正在重写aof_last_rewrite_time_sec:上次重写耗时aof_current_rewrite_time_sec:当前重写耗时aof_last_bgrewrite_status:上次重写状态
持久化问题:
- bgsave耗时过长:可能磁盘性能问题
- AOF重写失败:检查磁盘空间
- 持久化间隔过长:调整保存策略
5. info replication
主从信息:
role:当前角色(master/slave)connected_slaves:从节点数量master_repl_offset:主节点复制偏移量repl_backlog_size:复制积压缓冲区大小
从节点信息:
master_link_status:连接状态master_last_io_seconds_ago:上次IO时间slave_repl_offset:从节点复制偏移量
复制问题:
- 主从延迟大:检查网络或从节点负载
- 连接断开:检查网络或主节点配置
- 复制积压不足:调整
repl-backlog-size
6. info stats
关键统计:
total_commands_processed:处理的命令总数instantaneous_ops_per_sec:每秒操作数total_net_input_bytes:总输入流量total_net_output_bytes:总输出流量rejected_connections:拒绝的连接数expired_keys:过期的键数量evicted_keys:驱逐的键数量
性能分析:
- QPS突降:可能发生阻塞
- 大量拒绝连接:超过maxclients限制
- 高驱逐率:内存不足
3.1.3 client list 命令
CLIENT LIST命令提供所有客户端连接的详细信息,是排查客户端相关问题的利器。
命令输出详解
完整字段说明:
- id: 客户端唯一ID(单调递增)
- addr: 客户端地址(IP:PORT)
- fd: 文件描述符
- name: 客户端名称
- age: 连接持续时间(秒)
- idle: 空闲时间(秒)
- flags: 客户端标志组合
- N: 普通客户端
- S: 从节点客户端
- O: 正在执行命令
- b: 阻塞状态
- u: 未阻塞状态
- A: 连接待关闭
- c: 连接待关闭
- db: 当前选择的数据库
- sub/psub: 订阅模式数
- multi: 事务状态(-1/0/1)
- qbuf: 查询缓冲区大小(字节)
- qbuf-free: 查询缓冲区剩余空间
- obl: 输出列表长度
- oll: 输出缓冲区对象数
- omem: 输出缓冲区内存使用量
- events: 事件类型(r/w)
- cmd: 最近执行的命令
典型问题排查
1. 输出缓冲区问题
- 症状:omem值持续增长
- 阈值:通常超过64MB需要关注
- 原因:
- 客户端处理速度慢
- 网络带宽不足
- 解决方案:
- 调整
client-output-buffer-limit - 优化客户端处理逻辑
- 使用
CLIENT KILL终止问题连接
- 调整
2. 阻塞客户端
- 识别:flags包含'b'
- 常见命令:BLPOP, BRPOP, BRPOPLPUSH
- 风险:长时间阻塞消耗资源
- 处理:
- 检查阻塞超时时间
- 考虑使用非阻塞替代方案
3. 空闲连接
- 识别:age大且idle大
- 风险:连接泄漏
- 处理:
- 设置
timeout参数 - 定期使用
CLIENT KILL清理
- 设置
4. 大查询缓冲区
- 识别:qbuf大且qbuf-free小
- 风险:可能导致OOM
- 原因:大批量命令或异常请求
- 处理:限制单次请求大小
高级使用技巧
-
过滤输出:
redis-cli client list | grep "omem=[0-9]\{7,\}"查找输出缓冲区超过1MB的客户端
-
定期监控:
while true; do redis-cli client list | wc -l; sleep 1; done监控连接数变化
-
结合其他工具:
redis-cli --latency-history配合检查延迟问题
-
客户端管理:
CLIENT SETNAME:为连接设置名称CLIENT GETNAME:获取连接名称CLIENT PAUSE:暂停客户端CLIENT KILL:关闭指定连接
通过这些工具的综合使用,可以全面掌握Redis的运行状态,快速定位各类性能问题和阻塞原因,为Redis的性能优化提供数据支持。
3.2 第三方排查工具详解
3.2.1 redis-cli --stat:深入解析实时监控
redis-cli --stat 命令是 Redis 官方提供的轻量级监控工具,它通过每秒轮询 Redis 服务器来获取关键指标,并以表格形式展示实时数据。这个命令特别适合快速诊断 Redis 服务状态,因为:
- 实时性:数据每秒更新一次,可以立即观察到指标变化
- 低开销:相比其他监控工具,它对 Redis 服务器的性能影响极小
- 关键指标覆盖:包含了 Redis 运行的核心指标
命令执行方式:
redis-cli --stat [-i <interval>] [-h <host>] [-p <port>] [-a <password>]
其中 -i 参数可以调整采样间隔(默认1秒)
典型输出示例及详细解读:
------- data ------ --------------------- load --------------------- - child -
keys mem clients blocked requests connections
12345 1.2G 120 0 156789 (+0) 125
12345 1.2G 120 0 156792 (+3) 125
12345 1.2G 121 0 156795 (+3) 126
各列含义:
- keys:当前数据库中的键总数
- mem:Redis 进程使用的内存总量
- clients:当前客户端连接数
- blocked:被阻塞的客户端数量(如执行 BLPOP 等阻塞命令)
- requests:总命令请求数(括号内显示相较于上秒的增量)
- connections:自服务器启动以来接受的连接总数
在实际排查中,我们特别需要关注以下异常模式:
-
命令处理速率骤降:当
requests列的增量(括号内数值)突然从几百降到个位数,表明 Redis 可能遇到了严重阻塞,无法及时处理请求。这种情况常见于执行了耗时命令(如 KEYS *)或发生了长时间的持久化操作。 -
内存压力信号:当
mem持续增长并接近 maxmemory 配置值时,Redis 会开始执行内存淘汰策略。如果配置了淘汰策略为allkeys-lru等需要扫描键空间的策略,可能引起间歇性延迟。这时候需要检查内存使用模式,考虑是否要扩容或优化数据结构。 -
客户端阻塞:
blocked列显示非零值时,说明有客户端正在等待阻塞操作完成。如果该数值持续增长,需要检查是否有客户端执行了长时间的 BLPOP、BRPOP 等命令,或者订阅了大量频道的 PUB/SUB 客户端。
3.2.2 top/htop:系统资源监控深度应用
top 和 htop 是 Linux 系统下最常用的进程监控工具,它们可以提供 Redis 进程及其运行环境的全面资源使用情况。
基本用法对比
| 功能 | top | htop |
|---|---|---|
| 安装 | 系统自带 | 需要额外安装(yum install htop或apt-get install htop) |
| 交互性 | 基本 | 丰富,支持鼠标操作 |
| 可视化 | 文本 | 彩色显示,更直观 |
| 排序 | 有限 | 可按任意列排序 |
| 功能 | 基本进程管理 | 支持进程树查看、批量操作等 |
对于 Redis 排查,推荐使用 htop,因为它提供更直观的资源展示和更友好的交互体验。
关键指标解析
在 htop 界面中,我们需要特别关注以下信息:
-
Redis 进程行:
- CPU%:Redis 进程的 CPU 使用率
- MEM%:Redis 进程的内存使用占比
- RES:常驻内存大小(实际使用的物理内存)
- VIRT:虚拟内存大小(包括共享库等)
-
系统摘要行:
- Load average:系统负载(1/5/15分钟平均值)
- Tasks:总进程数及运行状态分布
- CPU states:CPU 使用分布(用户/系统/等待等)
- Memory:物理内存和交换空间使用情况
常见问题诊断模式
-
CPU 瓶颈:
- Redis 是单线程架构,正常情况下 CPU 使用率不应持续超过 90%
- 如果观察到单个 Redis 进程 CPU 持续高负载(如 95%+),可能原因:
- 执行了计算密集型命令(如 SORT、ZUNIONSTORE 等)
- 存在热点键被高频访问
- Lua 脚本执行时间过长
-
内存交换问题:
- 当系统的空闲内存不足时,Linux 会将部分内存页交换到磁盘
- 在 htop 中观察:
- Redis 的 RES 值波动较大
- Swap 使用量逐渐增加
- 系统内存的 used 接近 total
- 解决方案:
- 增加物理内存
- 调整系统 swappiness 参数
- 限制 Redis 使用的最大内存
-
IO 等待问题:
- 在 CPU states 中,如果 %wa(IO 等待)持续较高(如 >30%),表明磁盘 IO 成为瓶颈
- 常见于:
- AOF 持久化时的 fsync 操作
- RDB 生成时的磁盘写入
- 系统内存不足导致频繁交换
3.2.3 iostat:磁盘 IO 性能深度分析
iostat 是 Linux 系统提供的磁盘活动监控工具,特别适合分析 Redis 持久化操作对磁盘 IO 的影响。
基本命令参数
常用命令格式:
iostat -x -d [interval] [count]
其中:
-x:显示扩展统计信息-d:仅显示磁盘统计(不显示 CPU)interval:采样间隔(秒)count:采样次数
例如,每秒采样一次,共采样 10 次:
iostat -x -d 1 10
关键指标详解
典型输出示例:
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
vda 0.00 0.00 0.0 10.0 0.00 80.00 16.00 0.50 50.00 0.00 50.00 10.00 10.00
重要指标解释:
-
吞吐量指标:
r/s:每秒读操作数w/s:每秒写操作数rkB/s:每秒读取数据量(KB)wkB/s:每秒写入数据量(KB)
-
延迟指标:
await:平均 IO 响应时间(ms),包括排队和服务时间svctm:平均 IO 服务时间(ms)
-
利用率指标:
%util:设备繁忙百分比(100%表示饱和)
Redis 相关场景分析
-
RDB 持久化时:
- 观察
wkB/s突然增加,同时%util升高 - 如果
await超过 50ms,说明磁盘响应慢 - 解决方案:
- 使用更快的存储设备(如 SSD)
- 调整 RDB 触发条件(减少频率)
- 考虑关闭 RDB,只用 AOF
- 观察
-
AOF 重写时:
- 会同时产生大量读和写操作
- 观察
rkB/s和wkB/s同时增加 - 如果
%util持续 100%,考虑:- 使用
no-appendfsync-on-rewrite yes配置 - 分散 AOF 重写时间(避免高峰时段)
- 使用
-
磁盘性能基线:
- 建议在系统空闲时记录基准指标
- 使用命令:
iostat -x -d 1 10 > disk_baseline.log - 后续可对比异常时的指标差异
3.2.4 netstat/ss:网络连接深度排查
在 Redis 阻塞问题排查中,网络连接状态分析是重要环节。netstat 和 ss 是两种常用的网络工具,其中 ss 是更现代的替代方案,性能更好。
基本命令对比
| 功能 | netstat 命令 | ss 命令 |
|---|---|---|
| 查看所有 TCP 连接 | netstat -ant | ss -t -a |
| 查看监听端口 | netstat -lnt | ss -lnt |
| 按状态统计 | `netstat -ant | awk '{print $6}' |
| 查看进程信息 | netstat -antp | ss -antp |
| 查看连接数 | `netstat -ant | wc -l` |
Redis 特定分析
-
连接数统计:
# 查看 Redis 默认端口(6379)的连接数 ss -ant src :6379 | wc -l # 按状态分类统计 ss -ant src :6379 | awk '{print $1}' | sort | uniq -c -
连接来源分析:
# 查看前10个连接最多的客户端IP ss -ant src :6379 | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr | head -
连接持续时间:
# 查看已建立连接的持续时间(秒) ss -ant src :6379 | grep ESTAB | awk '{print $NF}' | cut -d: -f2 | sort -n
常见问题诊断
-
连接数过多:
- 现象:ESTABLISHED 状态连接数超过 maxclients 的 80%
- 可能原因:
- 客户端未正确释放连接
- 连接池配置过大
- 客户端实例过多
- 解决方案:
- 检查客户端连接管理
- 适当增加
maxclients - 使用连接池
-
TIME_WAIT 堆积:
- 现象:大量 TIME_WAIT 状态的连接
- 影响:占用端口资源,可能导致新连接失败
- 解决方案:
- 调整内核参数
net.ipv4.tcp_tw_reuse - 确保客户端主动关闭连接
- 增加本地端口范围
- 调整内核参数
-
网络吞吐瓶颈:
- 使用 iftop 工具监控实时流量:
iftop -i eth0 -P -n -N -f "port 6379" - 如果接近网络带宽上限,考虑:
- 升级网络设备
- 优化数据传输(如使用压缩)
- 分散 Redis 实例
- 使用 iftop 工具监控实时流量:
3.3 Redis 阻塞的排查流程详解
步骤 1:初步判断阻塞现象
典型阻塞表现
-
客户端症状:
- 连接超时错误(如 "Connection timed out")
- 读取超时错误(如 "Read timed out")
- 响应时间从毫秒级突增到秒级
-
服务端症状:
- QPS 突然下降 50% 以上
- Redis 日志出现 "慢查询" 警告
- 监控图表显示延迟突增
-
业务影响:
- 依赖 Redis 的业务接口超时率上升
- 用户投诉响应缓慢
- 监控系统告警触发
步骤 2:深入分析慢查询日志
慢查询配置检查
首先确认慢查询日志配置:
redis-cli config get slowlog-*
典型配置建议:
slowlog-log-slower-than 10000:记录超过 10ms 的查询slowlog-max-len 1024:保留最近的 1024 条慢查询
慢查询日志分析
获取最近的 100 条慢查询:
redis-cli slowlog get 100
输出示例:
1) 1) (integer) 1234 # 慢查询ID
2) (integer) 1634567890 # 发生时间戳
3) (integer) 25321 # 执行时间(微秒)
4) 1) "KEYS" # 命令
2) "*user*" # 参数
5) "127.0.0.1:58234" # 客户端
6) "" # 客户端名称
常见问题命令
-
全量查询命令:
KEYS *:全键扫描HGETALL大哈希表LRANGE大列表
-
计算密集型命令:
SINTERSTORE大集合ZUNIONSTORE大有序集- 复杂 Lua 脚本
-
大对象操作:
- 删除大对象(DEL 命令)
- 序列化/反序列化大对象
步骤 3:全面系统状态分析
info 命令组合使用
一次性获取所有关键信息:
redis-cli info all > redis_info_all.log
重点检查部分:
-
内存分析:
redis-cli info memory | grep -E 'used_memory|mem_fragmentation_ratio|maxmemory'used_memory_rss远大于used_memory表明内存碎片mem_fragmentation_ratio > 1.5需要关注
-
持久化状态:
redis-cli info persistence | grep -E 'aof_enabled|aof_rewrite_in_progress|rdb_bgsave_in_progress'- 检查是否正在进行 RDB/AOF 操作
-
复制状态:
redis-cli info replication | grep -E 'role|connected_slaves|master_link_status'- 主从同步延迟情况
步骤 4:客户端连接深度分析
client list 输出解析
获取详细客户端列表:
redis-cli client list > clients.log
典型输出字段:
id=123 addr=127.0.0.1:54321 fd=7 name= age=3600 idle=60 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=ping
关键字段分析:
omem:输出缓冲区内存使用(过大可能导致内存问题)cmd:最近执行的命令age:连接持续时间(秒)idle:空闲时间(秒)flags:标识(N=普通,b=阻塞等)
客户端问题定位
-
查找输出缓冲区大的客户端:
redis-cli client list | awk -F '=| ' '{if($20>1048576) print $0}' # 查找omem>1MB的客户端 -
查找长时间阻塞的客户端:
redis-cli client list | grep "flags=b" -
查找闲置连接:
redis-cli client list | awk -F '=| ' '{if($14>3600) print $0}' # 查找age>1小时的连接
步骤 5:系统资源综合检查
资源检查清单
-
CPU 检查:
- 使用
top -H -p $(pgrep redis-server)查看 Redis 线程 CPU 使用 - 检查 CPU 绑核情况(taskset 或 cgroup 配置)
- 使用
-
内存检查:
- 检查
/proc/[pid]/smaps了解详细内存分布 - 检查透明大页(THP)配置
- 检查
-
磁盘检查:
- 使用
df -h查看磁盘空间 - 使用
iotop查看实时 IO 进程
- 使用
-
网络检查:
- 使用
ethtool检查网卡状态 - 使用
nstat查看网络错误统计
- 使用
步骤 6:根因验证与解决方案
验证方法
-
慢查询复现:
- 在测试环境执行相同命令
- 使用
redis-benchmark模拟压力
-
配置调整测试:
- 临时修改
maxmemory-policy - 调整
timeout参数
- 临时修改
-
资源限制测试:
- 使用
cgroups限制资源 - 模拟网络延迟(
tc命令)
- 使用
常见解决方案
-
命令优化:
- 用
SCAN替代KEYS - 分批处理大对象
- 用
-
架构调整:
- 增加副本分担读压力
- 考虑集群分片
-
参数调优:
- 调整
tcp-backlog - 优化
hz参数
- 调整
-
硬件升级:
- 使用 SSD 存储
- 增加 CPU 核心
- 提升网络带宽
四、Redis 阻塞问题的解决方案与最佳实践
4.1 针对内部操作阻塞的解决方案
4.1.1 慢查询命令的优化
避免使用高风险命令
- SCAN替代KEYS:KEYS *命令会阻塞Redis直到遍历完所有键,时间复杂度为O(n)。而SCAN命令采用游标迭代方式,每次只返回少量键,时间复杂度为O(1)每次迭代。例如线上用户数据查询场景:
SCAN 0 MATCH user:* COUNT 50 # 分批次获取50个用户数据 - HSCAN替代HGETALL:对于包含百万字段的Hash,HGETALL会返回所有字段导致阻塞。HSCAN示例:
HSCAN user:profile 0 COUNT 20 # 分批获取用户资料字段 - UNLINK替代DEL:删除10GB的键时,DEL会阻塞主线程,而UNLINK会立即返回并将删除操作放入后台队列。典型应用场景:
UNLINK large_cache_key # 异步删除大缓存对象
控制数据量
- 大对象分片存储:如用户会话数据可拆分为多个Hash存储:
HSET user:1:session part1 key1 value1 HSET user:1:session part2 key2 value2 - 业务层排序:对商品列表排序,推荐在应用层使用快速排序替代Redis SORT命令
监控慢查询
- 配置示例:
slowlog-log-slower-than 5000 # 记录超过5ms的命令 slowlog-max-len 1000 # 保留1000条慢日志 - 告警规则示例(Prometheus):
alert: RedisSlowQuery expr: increase(redis_slowlog_entries[1m]) > 10
4.1.2 持久化操作的优化
RDB持久化优化
- 生产环境配置示例:
save 900 1 # 15分钟至少有1个变更 save 300 100 # 5分钟至少有100个变更 stop-writes-on-bgsave-error no # 避免写入失败导致服务不可用 - fork优化:在32GB内存实例上,fork时间可超过1秒。解决方案:
- 使用物理机而非虚拟机(fork更快)
- 配置
vm.overcommit_memory=1避免fork失败
AOF持久化优化
-
写入策略对比:
策略 数据安全性 性能影响 适用场景 always 最高 严重 金融交易 everysec 高 中等 大多数业务 no 低 最小 缓存 -
AOF重写自动触发条件:
auto-aof-rewrite-percentage 100 # 增长100%触发 auto-aof-rewrite-min-size 64mb # 最小64MB
4.1.3 主从同步的优化
全量同步优化
- 缓冲区配置:
repl-backlog-size 256mb # 增大缓冲区 repl-backlog-ttl 7200 # 延长TTL至2小时 - 带宽限制(10MB/s示例):
repl-diskless-sync yes # 无盘复制 repl-diskless-sync-delay 5 # 等待更多从节点
读写分离架构
graph TD
A[主节点] -->|同步| B[从节点1]
A -->|同步| C[从节点2]
D[应用] -->|写请求| A
D -->|读请求| B
D -->|读请求| C
4.2 针对外部因素阻塞的解决方案
4.2.1 网络问题的优化
网络拓扑优化
- 同机房部署:确保客户端与Redis的往返延迟(RTT)小于1ms
- 跨地域方案:
graph LR 北京集群-->|专线|上海灾备集群 北京集群-->|公网|广州灾备集群
TCP参数调优
echo 1024 > /proc/sys/net/core/somaxconn # 连接队列
echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout # 快速回收连接
4.2.2 操作系统配置的优化
内存管理
- 透明大页(THP)禁用:
echo never > /sys/kernel/mm/transparent_hugepage/enabled - OOM Killer配置:
echo -17 > /proc/[pid]/oom_adj # 防止Redis被杀死
文件系统优化
- 挂载参数(ext4):
noatime,data=writeback,barrier=0,nobh - IO调度器(SSD推荐):
echo noop > /sys/block/sda/queue/scheduler
4.2.3 客户端问题的优化
连接池最佳实践
- Java Jedis配置:
JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(200); // 最大连接数 config.setMaxIdle(50); // 最大空闲 config.setMinIdle(10); // 最小空闲 config.setMaxWaitMillis(2000); // 获取连接超时
大Key预防措施
- 写入前检查:
def safe_set(key, value): if len(value) > 10*1024*1024: # 超过10MB raise Exception("Value too large") redis.set(key, value)
批量操作优化
- 管道(Pipeline)示例:
pipe = redis.pipeline() for i in range(1000): pipe.set(f'key_{i}', f'value_{i}') pipe.execute() # 单次网络往返
4.3 针对资源竞争阻塞的解决方案
4.3.1 分布式锁的优化
优化锁的实现逻辑
-
使用SET NX EX命令原子性获取锁
- 完整命令示例:
SET lock_key unique_value NX EX 10unique_value建议使用UUID或客户端ID,确保全局唯一- 过期时间(EX)应根据业务执行时间合理设置,既不能太短导致业务未完成锁就释放,也不能太长导致死锁
- 替代方案:Redlock算法,适用于多节点Redis环境,提供更强的可靠性保证
- 完整命令示例:
-
实现锁的自动续期
- 典型实现方式:
- 启动后台线程/协程,定期执行
EXPIRE命令 - 续期间隔建议为锁过期时间的1/3(如锁10秒过期,每3秒续期一次)
- 业务执行完毕应立即停止续期
- 启动后台线程/协程,定期执行
- 开源实现:Redisson库的
WatchDog机制
- 典型实现方式:
-
正确释放锁
- Lua脚本完整示例:
if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end - 注意事项:
- 必须使用原子性操作,避免GET和DEL之间锁过期被其他客户端获取
- 释放失败时应记录日志,便于排查问题
- Lua脚本完整示例:
减少锁的竞争频率
-
缩小锁的粒度
- 实例分析:电商库存系统
- 坏实践:
LOCK inventory(锁定整个库存表) - 好实践:
LOCK inventory_sku_12345(按SKU加锁)
- 坏实践:
- 进阶方案:分段锁(如将ID范围0-9999分为10段,每段1000个ID)
- 实例分析:电商库存系统
-
避免长时间持有锁
- 优化建议:
- 锁内只保留必须原子执行的操作(如库存扣减)
- 将数据准备、后续处理等非关键操作移出锁范围
- 对耗时操作设置超时机制
- 示例流程:
def deduct_stock(): # 准备数据(无锁) item = get_item_from_db(item_id) # 获取锁并执行核心逻辑 with redis_lock(item_id): if item.stock >= quantity: update_stock(item_id, -quantity) else: raise OutOfStockError # 后续处理(无锁) send_notification() update_statistics()
- 优化建议:
-
其他优化技巧
- 引入随机退避:竞争失败后随机等待一段时间重试
- 实现锁等待队列:公平锁避免饥饿问题
- 考虑乐观锁:对冲突率低的场景使用版本号机制
4.3.2 内存分配与回收的优化
选择合适的内存分配器
-
jemalloc配置详解
- 安装步骤:
# 安装jemalloc开发包 sudo apt-get install libjemalloc-dev # 编译Redis时指定 make MALLOC=jemalloc - 性能对比:
- 多线程环境下jemalloc内存碎片率比glibc低30-50%
- 分配速度在高并发场景下快2-3倍
- 安装步骤:
-
内存碎片管理
- 监控指标:
redis-cli info memory | grep fragmentation - 碎片整理方案对比:
方法 适用版本 影响 建议 memory purge 4.0+ 低 首选 config set activedefrag yes 4.0+ 中 持续整理 重启 全版本 高 最后手段
- 监控指标:
优化内存淘汰策略
-
淘汰策略选择指南
策略 适用场景 优点 缺点 volatile-lru 缓存系统 保留热点数据 需设置过期时间 allkeys-lru 纯缓存 简单有效 可能淘汰重要数据 volatile-ttl 时效性数据 优先淘汰旧数据 需精确TTL noeviction 持久存储 不丢失数据 可能OOM -
maxmemory配置建议
- 计算公式:
maxmemory = min(0.8 * 物理内存, 总内存 - 系统预留) - 典型配置:
物理内存 系统预留 Redis建议值 16GB 2GB 12GB 32GB 4GB 24GB 64GB 8GB 48GB
- 计算公式:
-
进阶优化
- 使用Hash Tag确保相关数据在同一分片
- 对大对象进行压缩(如使用Snappy算法)
- 对冷数据启用主动淘汰
4.4 Redis阻塞的预防与监控最佳实践
4.4.1 完善监控体系
核心指标监控
-
Prometheus指标详解
# redis_exporter配置示例 scrape_configs: - job_name: 'redis' static_configs: - targets: ['redis://localhost:6379'] metrics_path: /scrape params: check-keys: ['user:*', 'product:*'] # 监控特定key模式 -
关键指标阈值参考
指标 警告阈值 严重阈值 检查频率 内存使用率 >80% >90% 1m 连接数 >1000 >2000 30s 慢查询 >5/min >20/min 5m CPU使用 >70% >90% 30s
告警集成方案
- 多通道告警配置
# Alertmanager配置示例 route: receiver: 'all-alerts' routes: - match: severity: 'critical' receiver: 'sms-team' - match: alertname: 'RedisDown' receiver: 'oncall-duty' receivers: - name: 'all-alerts' webhook_configs: - url: 'https://qyapi.weixin.com/cgi-bin/webhook/send?key=xxx' - name: 'sms-team' webhook_configs: - url: 'https://sms-api/send'
4.4.2 定期性能压测
压测方案设计
-
场景化测试用例
# 混合读写测试 redis-benchmark -t set,get -n 1000000 -c 200 -q # 管道测试 redis-benchmark -n 1000000 -P 16 -q # 模拟生产key分布 redis-benchmark -n 1000000 -r 100000 -
结果分析维度
- 吞吐量(QPS)随并发数变化曲线
- 延迟分布(P50/P95/P99)
- 错误率与系统资源消耗关系
4.4.3 制定应急预案
故障处理流程
-
分级响应机制
级别 症状 响应时间 处理措施 P0 完全不可用 <5min 主从切换+回滚 P1 部分异常 <15min 扩容+限流 P2 性能下降 <1h 参数调优 -
灾备方案实施
主从架构部署示例
# 主节点配置
port 6379
requirepass masterpass
masterauth slavepass
# 从节点配置
port 6380
replicaof 127.0.0.1 6379
masterauth masterpass
requirepass slavepass
Redis Cluster分片策略
# 创建6节点集群(3主3从)
redis-cli --cluster create \
127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 \
127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1
备份恢复流程
# RDB备份
redis-cli save # 同步保存
# 或
redis-cli bgsave # 异步保存
# AOF备份
redis-cli bgrewriteaof
# 恢复步骤
1. 停止Redis
2. 拷贝备份文件到工作目录
3. 修改配置文件
4. 启动Redis
3449

被折叠的 条评论
为什么被折叠?



