Redis 连接池被占满(泄漏)问题排查

一、现象

如题,导致所有请求都10s后报500错误,查询日志,大量报错org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is org.springframework.data.redis.connection.PoolException: Could not get a resource from the pool; nested exception is java.util.NoSuchElementException: Timeout waiting for idle object”

二、核心原因及解决方案

原因1:连接池配置过小,无法满足并发需求

这是最常见原因,并发请求数超过连接池最大连接数且连接未及时归还,导致连接池耗尽。

解决方案:调整Spring Boot连接池配置(application.yml)

spring:
  redis:
    # Redis基础配置
    host: 127.0.0.1
    port: 6379
    password: your_password # 无密码则注释
    database: 0
    timeout: 5000ms # 连接超时时间(默认2000ms,可适当延长)
    # Lettuce连接池配置(Jedis配置类似,替换lettuce为jedis即可)
    lettuce:
      pool:
        max-active: 200    # 最大活跃连接数(默认8,按业务峰值调整)
        max-idle: 50       # 最大空闲连接数
        min-idle: 10       # 最小空闲连接数
        max-wait: 3000ms   # 获取连接最大等待时间(默认-1无限等待,建议设置)
      shutdown-timeout: 100ms # 连接关闭超时时间

原因2:连接泄漏(连接未释放)

代码中获取连接后未正确关闭,导致连接长期占用,最终耗尽连接池。

检查&修复方法:

  1. 优先使用Spring管理的RedisTemplate,避免手动创建连接;
  2. 若手动操作Jedis连接,必须在finally块释放:
@Autowired
private JedisPool jedisPool;

public void redisOperation() {
    Jedis jedis = null;
    try {
        jedis = jedisPool.getResource();
        // 执行Redis操作
        jedis.set("key", "value");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (jedis != null) {
            jedis.close(); // 关键:释放连接回池
        }
    }
}

原因3:Redis服务/网络不可用

Redis宕机、防火墙拦截、网络延迟过高,导致连接池连接失效且无法创建新连接。

排查步骤:

  1. 测试连通性:redis-cli -h {host} -p {port} ping(正常返回PONG);
  2. 检查Redis服务状态:systemctl status redis(Linux);
  3. 查看Redis日志:/var/log/redis/redis-server.log(默认路径);
  4. 测试网络:telnet {host} 6379ping {host}

原因4:超时参数配置不合理

Redis操作耗时过长或连接超时参数过短,导致连接无法回收。

优化: 在上述YAML配置中调整timeout(连接超时)和max-wait(等待连接超时)参数。

三、关键监控手段(定位连接异常)

1. 监控Redis Established连接数

established是TCP层面连接状态,用于快速排查连接数是否异常:

# 方法1:netstat(统计6379端口ESTABLISHED连接数)
netstat -an | grep 6379 | grep ESTABLISHED | wc -l
# 详细输出(含客户端IP/端口)
netstat -antp | grep 6379 | grep ESTABLISHED

# 方法2:ss(更高效,按IP分组统计连接数)
ss -an | grep 6379 | grep ESTAB | wc -l
ss -an | grep 6379 | grep ESTAB | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr

# 方法3:Redis内置命令(精准,排除非Redis连接)
redis-cli INFO clients
# 核心字段:connected_clients(当前客户端连接数)、blocked_clients(阻塞连接数)

2. 执行CLIENT LIST命令(精准定位异常连接)

该命令列出所有连接的详细信息,是排查连接泄漏/并发过高的核心:

# 基础执行(本地Redis)
redis-cli CLIENT LIST
# 远程Redis执行
redis-cli -h 192.168.1.100 -p 6379 -a your_password CLIENT LIST

# 筛选异常连接(示例)
# 1. 空闲超300秒的连接(疑似泄漏)
redis-cli CLIENT LIST | grep -E "idle=[3-9][0-9]{2}|idle=[1-9][0-9]{3,}"
# 2. 特定IP的连接
redis-cli CLIENT LIST | grep "192.168.1.200"
# 3. 按IP统计连接数
redis-cli CLIENT LIST | awk -F'[ =]' '{print $4}' | cut -d: -f1 | sort | uniq -c | sort -nr
CLIENT LIST核心字段解读(泄漏特征)
字段含义排查重点
id连接ID唯一标识连接
addr客户端IP:端口定位异常连接的来源服务器
age连接建立时长(秒)数值过大说明连接长期未释放
idle连接空闲时长(秒)idle接近age:连接建立后未使用(泄漏);idle过小:连接持续被占用(并发高)
cmd最后执行的命令定位耗时操作(如大key的GET/SET)

泄漏特征:如果发现大量连接 age 值非常大(比如几天甚至几周),但同时 idle 值也非常大,这些很可能就是泄漏的连接——它们很早就被创建,但长期闲置没有被归还到池中。

进阶:关闭异常连接
# 按连接ID关闭
redis-cli CLIENT KILL 1234
# 按IP:端口关闭
redis-cli CLIENT KILL 192.168.1.200:54321
# 批量关闭空闲超300秒的连接
redis-cli CLIENT LIST | awk -F'[ =]' '{if($18>300) print "CLIENT KILL "$4}' | redis-cli

四、排查流程总结

  1. 先用redis-cli INFO clients查看connected_clients是否接近连接池max-active,确认连接池是否耗尽;
  2. ss/CLIENT LIST统计应用服务器连接数,区分是“并发过高”(idle小)还是“连接泄漏”(idle接近age);
  3. 查看CLIENT LISTcmd字段,定位慢查询或耗时长的操作;
  4. 针对性调整:并发高则调大连接池参数,连接泄漏则修复代码释放逻辑,网络/服务问题则排查Redis服务和网络。

关键点回顾

  1. 核心问题是Redis连接池无可用连接,优先检查max-active/max-wait等连接池配置;
  2. 连接泄漏是高频诱因,务必确保所有Redis连接使用后正确释放;
  3. INFO clientsCLIENT LIST是定位异常连接的核心命令,可快速区分并发过高/连接泄漏/服务不可用等根因。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值