connected_clients持续上涨是Redis连接泄露的最直接信号;需多次执行info clients观察趋势,结合CLIENT LIST查idle过大或来源集中的异常连接,并检查Jedis手动获取是否漏close、Lettuce是否误new客户端、线程是否阻塞在getResource。

看 connected_clients 是否持续上涨
这是最直接的信号:如果 Redis 服务端的连接数只增不减,基本坐实了泄露。用 redis-cli -a yourpass info clients 查看 connected_clients 字段,隔几十秒多跑几次,观察趋势。别只看一次——有些泄露是缓慢积累的,尤其低并发内部系统,可能几天才涨到几千。
顺手再跑 CLIENT LIST,重点关注 idle 值极大(比如 >3600 秒)或 addr 来源集中在某台应用机器的连接。这些大概率是“借出去没还”的连接。
- 运维常忽略一点:Spring Boot 默认启用
Lettuce客户端,它底层是基于 Netty 的连接复用,connected_clients上涨 ≠ 每次操作都建新连接,但若持续上涨,说明连接未被正确回收或连接池未生效 - 如果用的是老项目配的
Jedis,connected_clients几乎等于活跃连接数,上涨就更危险 - 注意区分:连接数突增也可能是短时压测或主从切换重连风暴,要结合时间点和业务变更日志交叉判断
查 Spring 中 RedisTemplate 或 Jedis 获取逻辑是否漏 close
Spring 管理的 RedisTemplate 本身不涉及手动释放,但一旦你绕过它、直接从 JedisPool 拿连接,就必须确保归还——哪怕在异常分支里。
典型错误写法:
public void badExample() {
Jedis jedis = jedisPool.getResource();
try {
jedis.set("key", "val");
} catch (Exception e) {
log.error(e);
// ❌ 忘了 close!连接永远卡在 borrowed 状态
}
}
正确做法必须带 finally 或用 try-with-resources:
public void goodExample() {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
jedis.set("key", "val");
} finally {
if (jedis != null) jedis.close(); // ✅ 归还给池,不是销毁
}
}
-
jedis.close()在 Jedis 3.x+ 中等价于returnResource,不是关 TCP;Jedis 2.x 需显式调用returnResource,否则会报错 - 用
@Autowired RedisTemplate是安全的,但若项目混用了StringRedisTemplate+ 手动Jedis,容易在同一个 Bean 里漏掉清理 - 特别警惕
RedisMessageListenerContainer:它内部持有一个长期连接,若配置了错误的重试策略(如无限重连),会在后台偷偷建大量连接
抓线程堆栈看是不是卡在 Pool.getResource
当应用变慢、CPU 不高但线程数飙升时,立刻 jstack <pid> > jstack.txt,然后搜 getResource:
cat jstack.txt | grep 'getResource' | wc -l
如果返回几百甚至上千,说明大量线程堵在取连接这一步——不是没连接可用,就是连接池满了,根源还是已有连接没归还。
- 此时别急着调大
maxTotal,先确认是不是连接泄漏;盲目扩容只会让问题延迟暴露 - 配合
jmap -histo:live <pid>看堆里有没有大量Jedis或DefaultEndpoint实例,能佐证是否对象没被 GC - Lettuce 用户注意:
StatefulRedisConnection是线程安全的,可复用;但若每次请求都 new 一个RedisClient,就必然泄漏
用 Prometheus + Grafana 监控连接池指标
光靠人工查太被动。Lettuce 和 Jedis 都支持暴露连接池指标,Spring Boot Actuator + Micrometer 可自动采集:
-
lettuce.connections.active:当前已借出连接数 -
lettuce.connections.idle:空闲连接数 -
lettuce.connections.pending:等待获取连接的线程数(>0 就该告警)
配置示例(application.yml):
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
spring:
redis:
lettuce:
pool:
max-active: 32
max-idle: 16
min-idle: 4
关键在于设好告警阈值:比如 lettuce.connections.active 持续 5 分钟 >90% max-active,就得触发钉钉/企微通知。
最容易被忽略的是:监控只接了指标,但没关联到具体业务接口。建议在关键 Redis 调用前打 Tracing 标签,一旦发现某接口的平均连接等待时间突增,立刻定位代码行。
连接泄露从来不是“有没有释放”这么简单,而是释放时机、连接生命周期、客户端选型、监控盲区四者叠加的结果。越早把 connected_clients 和线程栈纳入日常巡检,越不容易等到 OOM 才发现。

















