0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一次Redis连接数打满导致业务雪崩的排查记录

马哥Linux运维 来源:马哥Linux运维 2026-05-08 14:27 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

问题背景

某在线教育平台在一个工作日上午 10:00(业务高峰时段)收到大量线上报警:

用户端:页面加载失败、提交作业超时

服务端:Java 应用频繁报出redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool

Redis 监控:connected_clients达到上限maxclients=10000,rejected_connections开始出现

更严重的是,Redis 连接数打满后,连锁导致依赖 Redis 的认证服务、会话服务、缓存服务全部不可用,流量进一步转移到剩余的正常服务,引发了小范围的业务雪崩。

一、Redis 连接机制快速理解

1.1 Redis 如何处理连接

Redis 是单线程事件循环模型(6.0 之后网络 IO 可多线程,但核心处理仍是单线程)。每个客户端连接占用一个文件描述符(fd),Redis 通过epoll或kqueue处理事件。

关键配置项:

maxclients 10000    # 最大连接数(默认 10000,Redis 2.4+)
timeout 0        # 连接空闲超时(0 表示永不超时)
tcp-keepalive 300    # TCP keepalive 间隔

1.2 连接数打满的影响

当连接数达到maxclients后,Redis 不再接受新连接,并直接在日志中输出:

# Redis 9001 refused connection (maxclients)
-ERR max number of clients reached

此时所有依赖 Redis 的服务都会受到影响:

新请求无法获取 Redis 连接 → 业务线程阻塞

阻塞线程不断重试 → 线程池打满 → Web 容器无可用线程

上游服务的健康检查失败 → 从注册中心摘除节点

流量转移到剩余节点 → 剩余节点的 Redis 连接也暴增 → 级联故障

二、排查过程

2.1 直接检查 Redis 连接状况

# 登录 Redis 查看连接数
$ redis-cli -h  -p 6379 -a  INFO clients
# Clients
connected_clients:10000
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
cluster_connections:0
maxclients:10000

connected_clients=10000且maxclients=10000,连接数已打满。

2.2 查看连接分布

# 列出所有客户端连接
$ redis-cli CLIENT LIST
id=12345 addr=10.0.1.12:54321 fd=23 name= age=12345 idle=456 flags=N ...
id=12346 addr=10.0.1.13:54322 fd=24 name= age=12300 idle=12 flags=N ...
...

输出列含义:

addr:客户端 IP 和端口

age:连接存在时间(秒)

idle:连接空闲时间(秒)

flags:N 表示普通客户端,M 表示主从,S 表示从库

按 IP 统计连接数:

# 统计各客户端 IP 的连接数
$ redis-cli CLIENT LIST | awk'{print $2}'| cut -d= -f2 | cut -d: -f1 | sort | uniq -c | sort -rn | head -10
 3000 10.0.1.12
 2800 10.0.1.14
 1500 10.0.1.13
 1200 10.0.1.15
  ...

2.3 查找僵尸连接

# 查找空闲时间大于 300 秒的连接
$ redis-cli CLIENT LIST | awk -F'[ =]''{for(i=1;i<=NF;i++) if($i=="idle") print $(i+1), $0}' | awk '$1 > 300'| head -20

# 更简洁的方式
$ redis-cli CLIENT LIST | grep -E"idle=[0-9]{4,}"

发现大量连接的idle值在 600~3600 秒之间,说明这些连接长时间空闲但没有被回收。timeout配置为 0(永不超时)。

注:Redis 7.0+ 引入CLIENT NO-TOUCH和CLIENT NO-EVICT指令,但timeout仍是控制僵尸连接的主要参数。

2.4 确认系统级限制

Redis 的maxclients受系统文件描述符限制和内核参数限制:

# Redis 进程的 fd 限制
$ cat /proc/$(pidof redis-server)/limits | grep"Max open files"
Max open files      10024        10024        files

# 系统级 fd 限制
$ cat /proc/sys/fs/file-max
100000

# 当前 fd 使用量
$ cat /proc/sys/fs/file-nr
30000  0  100000

Redis 进程的Max open files为 10024,与 maxclients 10000 非常接近(Redis 本身还需要占用少量 fd 用于监听端口、AOF 写入等)。

2.5 应用层排查

检查应用侧的 Redis 连接池配置:

 

    
     
     
 
 
 
 

3000 个应用实例(部署了 15 个节点 × 每个节点 maxTotal=200) × 连接复用不足 → 实际连接数远超预期。

三、根因分析

经过以上排查,确定本次事故由三个因素叠加导致:

3.1 直接原因:应用发布后连接数暴增

当天凌晨发布新版本后,应用启动时连接池的minIdle=10导致每个节点预先建立 10 条连接。发布方式是滚动更新,旧节点和新节点短暂共存,连接数翻倍。部分旧节点上的连接在优雅关闭时未正确释放,变成僵尸连接。

3.2 放大因素:timeout = 0

Redis 配置timeout=0(永不主动断开空闲连接)。大量僵尸连接(idle > 600s)未被回收,持续占用连接槽位。等到上午业务高峰到来时,新连接请求直接打到上限。

3.3 雪崩链路

Redis 连接满(maxclients=10000)
 → 新请求无法获取连接 → JedisConnectionException
 → 应用层无熔断降级 → 线程被重试阻塞
 → Web 容器线程池打满 → 健康检查接口超时
 → 注册中心摘除节点 → 流量转移到剩余节点
 → 剩余节点 Redis 连接数飙升 → 也打满
 → 服务全面不可用

四、快速止血方案

4.1 方案 A:临时增大 maxclients(推荐首选)

# 临时修改(重启后失效)
$ redis-cli CONFIG SET maxclients 20000

注意:增大 maxclients 的同时必须增大系统的 fd 限制:

# 临时修改 Redis 进程的 fd 限制
$ prlimit --pid $(pidof redis-server) --nofile=30000

# 或修改系统级限制
$ulimit-n 65535

永久修改/etc/security/limits.conf:

# 添加以下行
root    soft  nofile   65535
root    hard  nofile   65535
redis   soft  nofile   65535
redis   hard  nofile   65535

Redis 配置文件中的maxclients也要同步修改:

# /etc/redis/redis.conf
maxclients 20000

4.2 方案 B:启用 timeout 自动清理僵尸连接

# 设置空闲超时 60 秒(立即生效)
$ redis-cli CONFIG SET timeout 60

60 秒后,所有空闲超过 60 秒的连接会被 Redis 自动关闭。这会触发以下效果:

# 观察连接数变化
$ watch -n 5'redis-cli INFO clients | grep connected_clients'

timeout值应根据业务心跳间隔设置。一般建议:

Web 应用 + 连接池:timeout=60~300

长连接订阅/推送:timeout=600~3600

仅做缓存:timeout=60

4.3 方案 C:批量清理异常连接

如果上述方案来不及等待,直接按条件 kill 连接:

# 按类型 kill(kill 所有普通客户端连接,保留主从复制连接)
$ redis-cli CLIENT KILL TYPE normal

# 按 IP 段 kill(如果某台应用服务器连接异常)
$ redis-cli CLIENT KILL addr 10.0.1.12:0

# 跳过 skipme(是否 kill 当前连接,默认 yes)
$ redis-cli CLIENT KILL addr 10.0.1.12:0 skipme no

4.4 方案 D:重启应用(让连接池重建)

作为兜底方案,重启应用让连接池重新初始化:

# 重启应用服务(确保是滚动重启)
$ systemctl restart app-service

重启后连接数会在短时间内回归正常水平(minIdle 重新建立)。

五、长期治理方案

5.1 连接池最佳实践

生产环境合理的连接池配置:


  
     
     
      

  
  

  
  
 
 
 

  
 

配置原则:

maxTotal 不要过大:单个应用节点对单个 Redis 实例的 maxTotal 建议 20~100,视并发度而定。连接数不是越多越好,Redis 处理 10000 个空闲连接和 500 个活跃连接的开销完全不同。

设置 maxWaitMillis:避免线程无限等待连接,建议 1000~3000ms。

开启 testWhileIdle:定时检测空闲连接是否可用,避免连接被 Redis 侧关闭后应用仍在使用。

代码中正确归还连接:使用 try-with-resources(Jedis 3.x+ 支持)或 finally 块确保 close。

// Jedis 3.x 推荐用法
try(Jedis jedis = jedisPool.getResource()) {
  jedis.set("key","value");
}// 自动归还连接,无需显式 close

// Jedis 2.x 必须显式 close
Jedis jedis =null;
try{
  jedis = jedisPool.getResource();
  jedis.set("key","value");
}finally{
 if(jedis !=null) {
    jedis.close(); // 归还到连接池而非真正关闭
  }
}

5.2 使用连接代理收敛架构

如果有大量客户端直连 Redis,考虑引入连接代理层:

[应用实例] x 50个 → [Twemproxy / Predixy] → [Redis 主从]

优势:

连接数从 M × N 降为 M + N(M 是应用节点,N 是 Redis 节点)

代理层可以缓冲突发连接请求

支持读写分离和故障转移

缺点:

增加一层网络跳转,延迟增加 0.1~0.5ms

代理本身可能成为瓶颈

5.3 连接限额和防火墙防护

# 限制单台应用服务器的 Redis 连接数(iptables)
$ iptables -A INPUT -p tcp --dport 6379 -m connlimit --connlimit-above 50 -j REJECT

5.4 熔断降级和重连退避

应用侧必须实现熔断机制:

// 使用 Resilience4j CircuitBreaker
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
  .failureRateThreshold(50)     // 50% 失败率触发熔断
  .waitDurationInOpenState(Duration.ofSeconds(30)) // 熔断后等 30 秒
  .slidingWindowSize(10)
  .build();

// 或简单的退避策略
intbaseDelay =100; // 基础等待 100ms
for(inti =0; i < maxRetries; i++) {
    try {
        return jedisPool.getResource();
    } catch (Exception e) {
        Thread.sleep(baseDelay * (long)Math.pow(2, i));  // 指数退避
    }
}

5.5 监控和告警

必须监控的 Redis 连接指标:

# Prometheus redis_exporter 已暴露的关键指标
redis_connected_clients
redis_config_maxclients
redis_rejected_connections_total

告警阈值建议:

指标 告警阈值 严重级别
connected_clients/maxclients > 80% Warning
connected_clients/maxclients > 90% Critical
rejected_connections > 0 立即 P0 Emergency

六、生产环境注意事项

CONFIG SET maxclients需要联动调整多个参数:

缺了任何一个,maxclients设置不生效。

Redis 配置maxclients

系统的ulimit -n

内核的fs.file-max

/etc/security/limits.conf

不要在高峰期修改 timeout:如果timeout从 0 改为一个很小的值(如 30),会瞬间断开大量连接,导致客户端连接池出现批量重建连接的场景,可能触发 CPU 暴涨和网络抖动。

CLIENT KILL 需要注意 skipme:如果当前连接也在 kill 范围内,会导致当前命令执行中断。默认 skipme=yes 不 kill 当前连接。

云 Redis 服务的 maxclients 限制:各云厂商 Redis 实例的maxclients可能与实例规格绑定(如 2GB 实例 = maxclients 10000),不能无限上调。提工单前先确认产品文档。

连接池泄漏排查:如果频繁出现连接打满但CLIENT LIST看不出来异常,检查应用代码中是否将 Jedis 实例作为成员变量而非局部变量使用,或者异常分支未执行 close。

七、总结

Redis 连接数打满导致业务雪崩的排查和治理可以分为三层:

第一层:快速止血

CONFIG SET maxclients 20000  # 增大上限
CONFIG SET timeout 60     # 清理僵尸连接
CLIENT KILL TYPE normal    # 批量杀异常连接

第二层:排查根因

检查连接分布 → 分析空闲时间 → 核对连接池配置 → 确认系统限制

第三层:架构治理

连接池规范化 → 熔断降级 → 代理收敛 → 监控告警 → 应急预案

本次故障的核心教训是:

timeout=0在生产环境是高风险配置,必须设置合理的空闲超时

连接池的 minIdle/maxTotal 要根据实际并发度计算,而非随意配置

服务必须有熔断降级机制,否则单一组件故障会级联扩散

发布过程中要监控连接数变化,发现异常立即回滚或调整

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 网络
    +关注

    关注

    14

    文章

    8341

    浏览量

    95619
  • 线程
    +关注

    关注

    0

    文章

    511

    浏览量

    20878
  • Redis
    +关注

    关注

    0

    文章

    395

    浏览量

    12262

原文标题:一次 Redis 连接数打满导致业务雪崩的排查记录

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    2011全球移动连接数将增至56亿

    今年全球移动连接数(Mobile Connection)将达到56亿,较2010年的50亿增长11%。移动数据服务收入将达到3147亿美元,较2010年的2570亿美元增长22.5%。
    发表于 08-05 09:21 1272次阅读

    ESP32-C3超过了最大站点连接数如何解决?

    C3为服务端,个设备不停的断开连接再重新连接C3,站点数也会增加,在断开连接时并不会减少站点数.导致多次以后,超过了最大站点
    发表于 06-20 08:23

    请问ESP32蓝牙连接数如何设置?

    ESP32 SPP 蓝牙连接数如何设置,蓝牙连接数设成大于1时,手机和ESP32蓝牙配对和数据通信都正常。把蓝牙设备连接改成1时,手机能够与ESP32蓝牙配置,但不能进行数据通信了。 make
    发表于 06-28 07:50

    企业打开Redis的正确方式,来自阿里云云数据库团队的解读

    的送礼物、讨论评论等大部分数据操作都适合非关系型数据库,而仅有充值等少量场景需要关系性数据库,因此重度依赖Redis数据库,相应地则有“稳定性高于切、低内存高QPS、连接数较大”的技术要求。相应
    发表于 02-07 14:06

    请问如何让从零开始递增每递增一次间隔1s记录一次记录完成再进行递增依次循环

    如何让从零开始递增,每递增一次间隔1s记录一次(此处记录只有值为T再为f就行),
    发表于 08-20 13:38

    请问如何分析、排查、解决Redis变慢问题

    如何分析、排查、解决Redis变慢问题
    发表于 11-09 08:03

    防火墙的并发连接数

    防火墙的并发连接数              并发连接数是指防火墙或代理服务器对其业务信息流的处理能力,是防火墙能够
    发表于 01-08 10:35 1051次阅读

    [Ganglia监控扩展]监控nginx的连接数

    [Ganglia监控扩展]监控nginx的连接数
    发表于 09-05 10:36 7次下载
    [Ganglia监控扩展]监控nginx的<b class='flag-5'>连接数</b>

    什么是空间_什么是空间流量_什么是连接数

    本文将详细介绍关于空间,空间流量,连接数等相关服务器,虚拟主机以及网站的相关知识。
    发表于 01-19 10:59 2722次阅读

    中国三大运营商NB-IoT连接数占全球连接数总量的30%

    目前,在整个中国物联网发展的进程中,尤其是NB-IoT方面,运营商的作用至关重要,据GSMA预测,中国三大运营商NB-IoT连接数占全球连接数总量的30%。
    发表于 01-14 09:26 4526次阅读

    glibc导致的堆外内存泄露的排查过程

    本文记录一次glibc导致的堆外内存泄露的排查过程。
    的头像 发表于 09-01 09:43 1685次阅读
    glibc<b class='flag-5'>导致</b>的堆外内存泄露的<b class='flag-5'>排查</b>过程

    Java redis锁怎么实现

    池的使用 在Java中使用Redis需要使用到相关的客户端库,比如Jedis、Lettuce等 配置Redis连接池的最大连接数、最大空闲连接数
    的头像 发表于 12-04 10:47 2137次阅读

    redis连接数配置多少合适

    Redis款高性能的内存数据库,广泛应用于缓存、消息队列、会话存储等场景。在配置 Redis 连接数时,需要根据实际情况综合考虑系列
    的头像 发表于 12-04 11:31 2424次阅读

    redis连接数对性能测试影响

    Redis个基于内存的键值存储数据库,它以其高性能和低延迟而闻名。在使用Redis进行性能测试时,连接数个非常重要的因素。
    的头像 发表于 12-04 11:33 2059次阅读

    记录一次K8s pod被杀的排查过程

    今天下午运维反馈说我们这个pod天重启了8,需要排查下原因。看Kiban日志,jvm没有抛出过任何错误,服务就直接重启了。
    的头像 发表于 01-18 09:57 1704次阅读