目录
一、现象
如题,导致所有请求都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:连接泄漏(连接未释放)
代码中获取连接后未正确关闭,导致连接长期占用,最终耗尽连接池。
检查&修复方法:
- 优先使用Spring管理的
RedisTemplate,避免手动创建连接; - 若手动操作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宕机、防火墙拦截、网络延迟过高,导致连接池连接失效且无法创建新连接。
排查步骤:
- 测试连通性:
redis-cli -h {host} -p {port} ping(正常返回PONG); - 检查Redis服务状态:
systemctl status redis(Linux); - 查看Redis日志:
/var/log/redis/redis-server.log(默认路径); - 测试网络:
telnet {host} 6379或ping {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
四、排查流程总结
- 先用
redis-cli INFO clients查看connected_clients是否接近连接池max-active,确认连接池是否耗尽; - 用
ss/CLIENT LIST统计应用服务器连接数,区分是“并发过高”(idle小)还是“连接泄漏”(idle接近age); - 查看
CLIENT LIST的cmd字段,定位慢查询或耗时长的操作; - 针对性调整:并发高则调大连接池参数,连接泄漏则修复代码释放逻辑,网络/服务问题则排查Redis服务和网络。
关键点回顾
- 核心问题是Redis连接池无可用连接,优先检查
max-active/max-wait等连接池配置; - 连接泄漏是高频诱因,务必确保所有Redis连接使用后正确释放;
INFO clients和CLIENT LIST是定位异常连接的核心命令,可快速区分并发过高/连接泄漏/服务不可用等根因。
423

被折叠的 条评论
为什么被折叠?



