connected_clients持续上涨即为连接泄露的最直接证据;需多次执行info clients观察趋势,结合client list查idle过大或来源集中的异常连接,并检查lettuce是否误new客户端、手动调用getstatefulconnection后未close、线程阻塞导致连接长期hold在borrowed状态。

connected_clients 持续上涨就是连接泄露,不是“可能”——这是最硬的证据。 隔 30 秒执行两次 redis-cli -a yourpass info clients,如果 connected_clients 从 1200 → 1247 → 1295,基本可以判定存在未回收连接。
怎么确认是 Lettuce 共享连接失效导致的泄露
Lettuce 默认开启 shareNativeConnection = true,同一线程内多次 RedisTemplate 调用应复用一个底层 Netty 连接,connected_clients 理论上应稳定在个位数(如 1~3)。一旦持续上涨,说明共享机制被绕过或卡死:
- 手动调用
connection.getStatefulConnection()后未归还——该连接不会自动释放,必须显式调用close() - 自定义
RedisConnectionFactory时未保留shareNativeConnection = true,退化为每请求新建连接 - 线程阻塞在数据库慢查、HTTP 调用等场景,导致连接长期
hold在 borrowed 状态;CLIENT LIST中可见大量idle > 3600且addr集中在同一台应用机器
Jedis 手动获取连接时漏 close 的典型写法
哪怕项目主体用 Lettuce,只要某处代码写了 jedisPool.getResource(),就必须确保归还。Jedis 3.x+ 的 close() 是归还连接,不是关 TCP;Jedis 2.x 则必须用 returnResource():
Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。
- 错误写法:
jedis.set("k","v");后无任何释放逻辑,异常分支也漏了 - 正确写法必须带
finally或用try-with-resources:try (Jedis jedis = jedisPool.getResource()) { jedis.set("k", "v"); } - 混用
StringRedisTemplate和手动Jedis时,尤其容易在同一个 Bean 里漏清理
连接池配置不当引发的“假死连接”堆积
Letture 连接池若未启用空闲驱逐,或等待与驱逐策略失配,会导致连接缓慢泄漏:
-
time-between-eviction-runs默认为-1(禁用),idle 连接永不回收,内存和文件描述符缓慢耗尽 -
max-wait: 3000ms+min-evictable-idle-time: 1s组合会引发“假死连接”:连接刚进 idle 就被驱逐,但因等待超时又无法及时归还,反复堆积 - 推荐配置(application.yml):
spring: redis: lettuce: pool: max-active: 200 max-idle: 50 min-idle: 10 max-wait: 3000ms time-between-eviction-runs: 30000ms min-evictable-idle-time: 60000ms
真正难排查的是线程卡死导致的连接长期 hold,它不报错、不抛异常,只让 CLIENT LIST 里的 idle 值悄悄爬升。这时候 jstack 抓堆栈比看日志更管用——搜 getResource 或 connectStandalone,往往一眼就能定位住卡在哪一行。










