Redis 故障排查与应急手册:从理论到实践


Redis 故障排查与应急手册:从理论到实战

场景:线上 Redis 集群出现性能抖动、连接异常、数据丢失等问题时的快速响应指南


一、故障分级与响应策略

在深入技术细节之前,先建立故障分级意识

级别 现象 响应时间 核心目标
P0 集群完全不可用,业务中断 5分钟内 快速恢复服务
P1 主节点宕机,自动切换中 15分钟内 确保高可用生效
P2 性能下降,延迟升高 30分钟内 定位根因并优化
P3 监控告警,指标异常 2小时内 预防性处理

二、核心排查工具箱

2.1 必知必会的监控指标

bash 复制代码
# 实时查看关键指标
redis-cli INFO stats
redis-cli INFO memory
redis-cli INFO replication
redis-cli INFO clients

黄金指标清单

指标 健康阈值 异常含义
used_memory / maxmemory < 85% 内存即将耗尽,可能触发驱逐
instantaneous_ops_per_sec 视业务而定 突增可能意味着热 Key 或攻击
connected_clients < 10000 连接数过多,可能连接泄漏
rejected_connections 0 连接被拒绝,检查 maxclients
keyspace_hits / keyspace_misses 命中率 > 90% 缓存失效严重
latest_fork_usec < 100000 Fork 耗时过长会阻塞主线程

2.2 慢查询分析

bash 复制代码
# 设置慢查询阈值(单位:微秒)
redis-cli CONFIG SET slowlog-log-slower-than 10000

# 查看最近 10 条慢查询
redis-cli SLOWLOG GET 10

# 重置慢查询日志
redis-cli SLOWLOG RESET

慢查询分析要点

  • 关注 KEYS *FLUSHALLHGETALL 等全量操作
  • 检查是否有 O(N) 命令操作大 Key
  • 复杂 Lua 脚本的执行时间

2.3 大 Key 扫描(线上慎用)

bash 复制代码
# 安全扫描大 Key(--bigkeys 是采样统计,非精确值)
redis-cli --bigkeys

# 更精确但需要遍历(低峰期使用)
redis-cli --memkeys-samples 1000

三、六大经典故障场景与应急方案

场景一:Redis 内存飙升 / OOM

现象used_memory 持续增长,最终触发 OOM 或大量 Key 被驱逐

排查步骤

bash 复制代码
# 1. 查看内存使用详情
redis-cli INFO memory

# 2. 查看 Key 的内存分布
redis-cli --bigkeys

# 3. 检查内存策略
redis-cli CONFIG GET maxmemory-policy

常见根因

根因 识别方法 解决方案
缓存未设置过期时间 redis-cli INFO keyspace 查看 expires 添加 TTL,清理无用数据
大 Key 问题 --bigkeys 发现单个 Key > 1MB 拆分 Hash,使用 HSCAN 分批读取
内存碎片率过高 mem_fragmentation_ratio > 1.5 重启实例或启用 activedefrag
写入缓冲区堆积 client-output-buffer-limit 超限 调整缓冲区限制,优化消费速度

应急操作

bash 复制代码
# 紧急设置内存上限(防止系统 OOM)
redis-cli CONFIG SET maxmemory 8gb

# 调整驱逐策略为 volatile-lru(优先淘汰有过期时间的 Key)
redis-cli CONFIG SET maxmemory-policy volatile-lru

# 手动删除大 Key(使用 UNLINK 非阻塞删除)
redis-cli UNLINK big_hash_key

# 开启主动碎片整理(Redis 4.0+)
redis-cli CONFIG SET activedefrag yes

场景二:Redis 连接数爆满

现象connected_clients 接近 maxclients,新连接被拒绝

排查步骤

bash 复制代码
# 查看当前连接数
redis-cli INFO clients

# 查看连接来源
redis-cli CLIENT LIST | grep -E "(addr|name|age|idle)"

# 统计各 IP 连接数
redis-cli CLIENT LIST | awk -F'=' '/addr=/{print $2}' | cut -d':' -f1 | sort | uniq -c | sort -rn | head -20

常见根因

根因 识别特征 解决方案
连接池配置不当 单服务节点连接数异常高 调整连接池 maxTotalminIdle
连接泄漏 idle 时间长但未被释放 检查代码中 close() 是否被调用
短连接风暴 大量 age 很小的连接 使用连接池,避免频繁创建连接
客户端 Bug 特定版本客户端异常 升级客户端版本

应急操作

bash 复制代码
# 临时提升最大连接数
redis-cli CONFIG SET maxclients 20000

# 踢掉空闲连接(危险操作,谨慎使用)
redis-cli CLIENT KILL TYPE normal IDEL 600

# 查看阻塞客户端
redis-cli CLIENT LIST | grep -i blocked

Java 连接池优化示例

java 复制代码
// Jedis 连接池配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);           // 最大连接数
config.setMaxIdle(50);             // 最大空闲连接
config.setMinIdle(10);             // 最小空闲连接
config.setTestOnBorrow(true);      // 借用时验证
config.setTestWhileIdle(true);     // 空闲时验证
// 关键:确保连接正确关闭
try (Jedis jedis = pool.getResource()) {
    jedis.get("key");
} // 自动归还连接

场景三:Redis 主从复制中断

现象 :从节点状态为 master_link_status:down,数据不同步

排查步骤

bash 复制代码
# 查看复制状态
redis-cli INFO replication

# 查看从节点日志
tail -f /var/log/redis/redis-server.log

# 检查主节点复制积压缓冲区
redis-cli INFO stats | grep -E "(master_repl_offset|repl_backlog)"

常见根因

根因 识别方法 解决方案
网络闪断 日志中出现 Connection timed out 调整 repl-timeoutrepl-ping-replica-period
复制缓冲区不足 master_repl_offset 差异大 增大 repl-backlog-size
从节点重启 master_link_statusdown 自动重连或手动 SLAVEOF
主节点 RDB 生成失败 日志中 Can't save in background 检查磁盘空间和 maxmemory

应急操作

bash 复制代码
# 从节点强制重新同步(会清空从节点数据)
redis-cli SLAVEOF NO ONE
redis-cli SLAVEOF master_host master_port

# 调整复制超时时间
redis-cli CONFIG SET repl-timeout 120
redis-cli CONFIG SET repl-ping-replica-period 30

# 增大复制积压缓冲区(默认 1MB,建议 100MB+)
redis-cli CONFIG SET repl-backlog-size 104857600

场景四:Redis 性能急剧下降

现象:RT 从 ms 级上升到秒级,甚至超时

排查步骤

bash 复制代码
# 查看 CPU 使用率(Redis 是单线程,CPU 100% 意味着忙碌)
top -p $(pgrep redis-server)

# 查看命令统计
redis-cli INFO commandstats

# 实时监控命令执行
redis-cli MONITOR | head -100  # 注意:生产环境慎用,性能开销大

# 查看延迟监控
redis-cli --latency-history -i 1

常见根因

根因 识别方法 解决方案
热 Key 访问 instantaneous_ops_per_sec 突增 本地缓存 + Key 拆分(如 key:{hash}
大 Key 操作 SLOWLOG 中出现 HGETALLSMEMBERS 拆分数据,使用 HSCANSSCAN
Fork 阻塞 latest_fork_usec 过大 控制实例内存大小,使用磁盘less复制
AOF 刷盘阻塞 aof_delayed_fsync 增加 调整 appendfsync 策略
持久化竞争 备份或 AOF rewrite 期间 避免高峰期执行 BGSAVE

应急操作

bash 复制代码
# 临时关闭 AOF(数据安全与性能的权衡)
redis-cli CONFIG SET appendonly no

# 调整 AOF 刷盘策略
redis-cli CONFIG SET appendfsync everysec  # 或 no(最高性能)

# 禁用 THP(透明大页)减少 Fork 耗时
echo never > /sys/kernel/mm/transparent_hugepage/enabled

# 使用管道批量操作(减少 RTT)
redis-cli --pipe < commands.txt

热 Key 解决方案架构

复制代码
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Client    │────▶│ Local Cache │────▶│  Redis      │
│             │     │ (Caffeine/   │     │  Cluster    │
│             │◀────│  Guava)     │◀────│             │
└─────────────┘     └─────────────┘     └─────────────┘
         │                                    │
         └────────── 异步更新缓存 ◀────────────┘

场景五:Redis 脑裂(Split-Brain)

现象:主从切换后,旧主节点仍在接受写请求,导致数据不一致

排查步骤

bash 复制代码
# 检查当前主从拓扑
redis-cli -h old_master INFO replication
redis-cli -h new_master INFO replication

# 检查 Sentinel 日志
tail -f /var/log/redis/sentinel.log

预防措施

bash 复制代码
# 配置 min-slaves 机制(Redis 2.8+)
redis-cli CONFIG SET min-slaves-to-write 1
redis-cli CONFIG SET min-slaves-max-lag 10

# 配置 Sentinel(自动故障转移)
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000

应急操作

bash 复制代码
# 强制旧主节点降级为从节点
redis-cli -h old_master SLAVEOF new_master_ip new_master_port

# 如果数据已不一致,需要权衡:保留旧数据还是新数据
# 方案1:保留新主节点数据,丢弃旧主节点数据
redis-cli -h old_master DEBUG SEGFAULT  # 强制崩溃重启,清空数据后同步

# 方案2:保留旧主节点数据,手动合并(复杂,需业务配合)

场景六:缓存雪崩 / 击穿 / 穿透

现象:大量请求直达数据库,DB 压力骤增

问题类型 现象 解决方案
缓存雪崩 大量 Key 同时过期 随机 TTL、多级缓存、熔断降级
缓存击穿 热点 Key 过期瞬间高并发 互斥锁、逻辑过期、永不过期
缓存穿透 查询不存在的 Key 布隆过滤器、空值缓存、参数校验

应急操作代码示例

java 复制代码
// 缓存击穿防护:互斥锁
public String getWithLock(String key) {
    String value = redis.get(key);
    if (value == null) {
        // 获取分布式锁
        if (redis.setnx("lock:" + key, "1", 10)) {
            try {
                value = db.get(key);
                redis.setex(key, 3600, value);
            } finally {
                redis.del("lock:" + key);
            }
        } else {
            // 等待后重试
            Thread.sleep(100);
            return getWithLock(key);
        }
    }
    return value;
}

// 缓存穿透防护:布隆过滤器
public String getWithBloomFilter(String key) {
    if (!bloomFilter.mightContain(key)) {
        return null; // 直接返回,不查 DB
    }
    // 正常查询缓存和 DB
}

四、标准化应急响应流程

复制代码
┌─────────────────┐
│   收到告警通知   │
└────────┬────────┘
         ▼
┌─────────────────┐     是      ┌─────────────────┐
│  服务是否可用?  │────────────▶│  立即切换流量   │
│                 │             │  到备用集群     │
└────────┬────────┘             └─────────────────┘
         │ 否
         ▼
┌─────────────────┐
│  查看监控大盘    │
│  (内存/CPU/连接) │
└────────┬────────┘
         ▼
┌─────────────────┐
│  定位故障场景    │
│  (匹配六大场景)  │
└────────┬────────┘
         ▼
┌─────────────────┐     是      ┌─────────────────┐
│  是否需要应急?  │────────────▶│  执行应急操作   │
│                 │             │  (保留现场日志) │
└────────┬────────┘             └─────────────────┘
         │ 否
         ▼
┌─────────────────┐
│  根因分析修复    │
│  更新故障案例库  │
└─────────────────┘

五、预防性建设清单

5.1 监控告警体系

yaml 复制代码
# Prometheus + AlertManager 配置示例
groups:
  - name: redis
    rules:
      - alert: RedisMemoryHigh
        expr: redis_memory_used_bytes / redis_memory_max_bytes > 0.85
        for: 5m
        annotations:
          summary: "Redis 内存使用率超过 85%"
          
      - alert: RedisConnectionsHigh
        expr: redis_connected_clients / redis_config_maxclients > 0.8
        for: 2m
        annotations:
          summary: "Redis 连接数超过 80%"
          
      - alert: RedisReplicationLag
        expr: redis_master_link_up == 0
        for: 1m
        annotations:
          summary: "Redis 主从复制中断"

5.2 配置最佳实践

bash 复制代码
# redis.conf 核心配置
maxmemory 8gb
maxmemory-policy allkeys-lru
maxclients 10000

# 持久化配置(根据业务选择)
appendonly yes
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 慢查询配置
slowlog-log-slower-than 10000
slowlog-max-len 128

# 客户端输出缓冲区
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

5.3 定期演练事项

  1. 月度:执行主从切换演练,验证 Sentinel/Cluster 自动故障转移
  2. 季度 :模拟大 Key 删除,验证 UNLINK 非阻塞效果
  3. 半年:全量数据恢复演练,验证 RDB/AOF 文件可用性

六、总结:故障排查心法

"监控先行,日志为凭,工具辅助,经验兜底"

  1. 保持冷静:先确认服务可用性,再深入排查
  2. 现场保护 :故障时的 INFOMONITORSLOWLOG 及时保存
  3. 变更关联:故障前是否有发布、配置变更、扩容操作
  4. 渐进修复:优先止损,再根治,避免二次故障
  5. 复盘归档:每个故障都是案例,更新到团队知识库

延伸阅读


本文基于 Redis 6.x/7.x 版本编写,部分命令在低版本可能略有差异。生产环境操作前,请务必在测试环境验证。

相关推荐
Li emily2 小时前
解决了用美股历史数据api分析价格波动的困扰
数据库·人工智能·python
茉莉玫瑰花茶2 小时前
MySQL 存储过程与触发器超详解:从基础到实战(含面试题 + 案例)
数据库·mysql
xiaokangzhe2 小时前
MySQL故障排查与优化
数据库·mysql
圣光SG2 小时前
Java类与对象及面向对象基础核心详细笔记
java·前端·数据库
2601_949818092 小时前
LangChain-08 Query SQL DB 通过GPT自动查询SQL
数据库·sql·langchain
ytttr8732 小时前
C# 读取数据库表结构工具设计与实现
开发语言·数据库·c#
白露与泡影2 小时前
从 BIO 到 epoll:高并发 I/O 模型演进与本质分析
java·服务器·数据库
知识分享小能手2 小时前
MongoDB入门学习教程,从入门到精通,MongoDB副本集的核心机制(11)
数据库·学习·mongodb
一 乐2 小时前
剧场管理系统|基于springboot + vue剧场管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·剧场管理系统