news 2026/9/30 7:55:12

Spring Boot Redis 连接池 PoolException 排查

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot Redis 连接池 PoolException 排查

线上跑着一个 Spring Boot 服务和一套 Redis,某天开始日志里每隔几秒就刷一条org.springframework.data.redis.connection.PoolException: Could not get a resource from the pool,接口 RT 从 20ms 飙到 3 秒,最后干脆大面积 500。这个异常我前前后后处理过不下十次,它出现的场景五花八门:有的是连接池参数拍脑袋写的,有的是 Redis 服务端被一条keys *顶住了,还有一次是运维改了安全组白名单,客户端死活连不上。这条报错的本质其实一句话就能说清——连接池借不到连接,但"为什么借不到"有至少七八种可能,而每一种的修法完全不一样,改错方向只会把问题拖得更久。这篇内容我打算按真实排查顺序走一遍:先把异常尾巴读明白,再把 Jedis 和 Lettuce 两套池子的参数算清楚,然后从客户端一路查到服务端,最后给出几个高频场景的完整复盘和一份速查表。不管你是刚接手的 Spring Boot 项目、正在做缓存治理,还是面试前想把 Redis 这块彻底捋一遍,都能直接从里面抄配置、抄排查命令。

1. 先把异常读透,别急着改参数

1.1 异常尾巴才是真正的线索

Could not get a resource from the pool这句话本身信息量几乎为零,它只是池子在喊"我这儿没货了"。真正有价值的是它下面Caused by的那一行,我习惯把它分成三档来看。

第一档是网络层报错,尾巴一般是java.net.ConnectException: Connection refused或者connect timed out、No route to host。这说明池子里的连接根本建立不起来,池子里的空闲连接被耗尽之后,新建连接又失败,于是对外统一表现为借不到。这类问题的排查重点在端口、地址、防火墙、DNS,跟池子参数一点关系都没有。

第二档是服务端拒绝,尾巴常见redis.clients.jedis.exceptions.JedisDataException: ERR max number of clients reached,或者客户端侧直接看到rejected_connections在涨。这说明能连,但服务端已经到maxclients上限,新连接一律被拒。同一时间所有客户端都会复现,而且往往是"本来好好的,突然全炸"。

第三档才是真正的池内问题,尾巴是JedisExhaustedPoolException: Could not get a resource since the pool is exhausted,或者压根没有Caused by,直接就是PoolException+Timeout waiting for idle object。这种情况说明池子里的连接都被借走没还,或者借的时候排队超时。只有这一档才需要去调池子参数,前两档调参数纯属浪费时间,甚至会把问题掩盖得更深。

有个小技巧:把PoolException打印的完整堆栈单独抓出来,搜Could not get a resource之后的第一行Caused by,90% 的情况下这一行就能直接定位方向,比看任何监控都快。

1.2 三分钟判断是池内还是池外

很多人一上来就maxTotal从 8 调到 200,结果 Redis 那边连接数直接冲上几千,服务端开始抖,问题更严重。给你一个三分钟的判断路径,按顺序做,不用猜。

判断动作结果结论方向
redis-cli -h host -p port ping从应用机器执行返回 PONG网络和服务端基本正常,重点看池内
同上超时或 refused网络/安全组/服务端挂了,跟池子无关
redis-cli info clients | grep connected_clients接近或等于 maxclients服务端连接数打满,池子只是受害者
redis-cli info stats | grep rejected_connections持续增长连接被拒,先加 maxclients 或查客户端泄漏
redis-cli slowlog get 10有明显几百毫秒的慢命令单线程被阻塞,所有连接一起排队
redis-cli info clients | grep blocked_clients大于 0 且持续有人用了 BLPOP 之类的阻塞命令,长期占着连接
以上全正常,应用侧 active 连接数顶到 maxTotal—真池内问题,开始调参和查泄漏

这个表我贴在自己团队的排查手册第一页。经验是:先排除池外,再动池内,顺序反了就容易误伤。

2. 连接池参数到底该怎么配

2.1 先确认你的配置真的生效了

这一步能救很多人。Spring Boot 2.4 之前,Redis 配置前缀是spring.redis.*;2.4 之后改成了spring.data.redis.*。如果你从老项目复制配置过来,前缀写错了,那一整段配置会被静默忽略,全部走默认值——而默认值通常是maxTotal=8、maxWait=-1(无限等待)。maxWait=-1意味着借不到连接时会一直挂着,线程池被慢慢占满,最后整个应用卡死,日志里刷的正是这条PoolException。

Jedis 场景的完整配置大概长这样:

spring: data: redis: host: 10.0.0.21 port: 6379 password: ${REDIS_PASSWORD} database: 0 timeout: 2000ms # 命令读写超时 connect-timeout: 1000ms # 建连超时 jedis: pool: max-active: 64 # 对应 maxTotal max-idle: 32 min-idle: 16 max-wait: 1000ms # 借连接最长等待,千万别写 -1 time-between-eviction-runs: 30s min-evictable-idle-time: 5m

Lettuce 场景如果你显式启用了池化,配置前缀是spring.data.redis.lettuce.pool,字段名和 Jedis 那套基本一致(max-active、max-idle、min-idle、max-wait)。要特别注意:Lettuce 默认是单连接共享模式,不加commons-pool2依赖、不写 pool 配置的话,它压根没有池子,多个线程复用一个 Netty 连接。这时候报PoolException就很奇怪了,说明你确实引入了池化。

验证配置生效最简单的办法:写一个@PostConstruct把JedisConnectionFactory或者LettuceConnectionFactory的配置对象打出来,或者直接在启动日志里打开logging.level.org.springframework.data.redis=DEBUG,看有没有打印池子的 maxTotal。别嫌土,这招帮我抓到过三次配置前缀写错的问题。

2.2 maxTotal 该设多少,得算不能拍

先说结论:maxTotal是并发连接数上限,不是 QPS 上限。用 Little's Law 算:并发数 = QPS × 平均耗时。假设你的服务峰值 QPS 是 3000,每次 Redis 操作平均耗时 0.5ms,那理论并发 = 3000 × 0.0005 = 1.5,也就是平均只有 1 到 2 个连接在同时工作。那是不是 maxTotal 设 4 就够了?不行,因为平均值会骗人。

真实流量有突发,命令耗时也有长尾。我的做法是取 P99 耗时来算:如果 P99 是 5ms,突发峰值 QPS 按 2 倍算,那就是 6000 × 0.005 = 30 个并发连接。再留一倍冗余应对网络抖动和扩容,maxTotal取 64 比较稳。如果单实例算出来超过 128,我反而建议先看看是不是有慢命令,而不是继续加连接——连接数堆上去,Redis 服务端的 CPU 花在上下文切换上的比例会明显上升。

maxIdle建议设成maxTotal的一半到全量。设太小会导致连接频繁创建销毁,你会在日志里看到连接建立耗时的抖动;设成等于maxTotal则意味着空闲时也不会回收,内存占用略高但延迟最稳。minIdle我一般设maxTotal的四分之一,目的是避免流量刚起来时"冷启动"批量建连,那段时间正好是借连接最容易超时的窗口。maxWait给 500ms 到 1s,必须设有限值。超过了直接抛异常快速失败,比线程无限堆积好得多——请求进来至少能立刻返回错误,线程池不会连带崩掉。

2.3 Jedis 和 Lettuce,池化行为差别很大

这两套客户端的连接模型完全不同,排查思路也不一样,很多人混着用导致判断失准。

Jedis 是"一个连接一个线程"的同步模型,每次命令都要从池子里借连接,用完归还。所以 Jedis 天生就是池化的,maxTotal就是它的命门,配置写得不对,PoolException会来得非常频繁。好处是行为直观,连接泄漏了 friendly,client list里能直接看到来源 IP 和命令数。

Lettuce 基于 Netty,是异步、多路复用的模型,默认所有线程共享一个连接,压根不需要池子。共享连接模式下不存在"借不到"的问题,但会出现另一种麻烦:一条耗时长的命令会把整个连接堵住,后续所有命令排队。如果你在 Lettuce 上启用了池化,maxTotal的语义就变成了"最多允许多少个独立连接",每个连接内部还是多路复用。

到底选哪个?我的经验是:普通读写为主、没有阻塞类命令的服务,Lettuce 默认单连接就很够用,配置简单、资源占用低。如果业务里有BLPOP、BRPOP、SUBSCRIBE这类长期占着连接的用法,或者有大批量 pipeline,那就用池化模式,把阻塞类操作隔离到独立连接上。另外老项目里jedis用得非常多,看到PoolException时先确认自己用的是哪套,再去看对应的配置段。

3. 按顺序排查:从客户端一路查到服务端

3.1 网络这一层必须最先确认

排查的第一步永远是连通性,别看它简单,我遇到的PoolException里有将近三成根源在网络。先在最靠近应用的机器上执行:

# 基础连通,建议直接用目标版本一致的 redis-cli redis-cli -h 10.0.0.21 -p 6379 -a "$REDIS_PASSWORD" --no-auth-warning ping # 没有 redis-cli 时用 nc 看端口通不通 nc -zv 10.0.0.21 6379

如果 ping 不通,先看四个地方:云安全组/网络 ACL 的出方向规则、Redis 侧的白名单、DNS 解析结果(dig或nslookup,确认解析到的是不是内网地址)、以及容器环境里的网络模式。我在 K8s 里踩过一次坑:Pod 的dnsPolicy配错,域名解析到了公网地址,端口自然不通,报错也是PoolException。

还有一种特别隐蔽的情况——连接建立慢但不失败。比如跨可用区访问,建连 RTT 有 30ms,池子冷启动阶段minIdle个连接要串行建立,maxWait只给了 200ms,那前几秒的请求必然大批借连接超时,几秒后又自己好了。这种"周期性抖动"很难查,建议看一下maxWait和建连耗时是否在同一数量级。

注意:排查连通性时用的redis-cli版本最好和服务端大版本一致。用 3.x 的客户端去连 7.x 的集群,某些命令会返回奇怪的错误,容易把你带偏。

3.2 服务端的连接数上限和内存水位

确认网络通了,下一步看服务端到底还能不能收新连接:

redis-cli -h 10.0.0.21 -p 6379 info clients # connected_clients:9821 # blocked_clients:3 # maxclients:10000 redis-cli -h 10.0.0.21 -p 6379 info stats | grep -E "rejected|expired" # rejected_connections:18342 redis-cli -h 10.0.0.21 -p 6379 config get maxclients redis-cli -h 10.0.0.21 -p 6379 client list | head -50

connected_clients逼近maxclients的时候,新连接会被直接拒绝并计入rejected_connections,客户端侧看到的就是连接池借不到资源。此时有两个动作:一是临时调大maxclients止血(config set maxclients 20000,注意这个值还受操作系统文件描述符限制,ulimit -n要同步调),二是回头查是谁把连接耗光了。

client list的输出里,重点看三类:addr相同的连接特别多(说明某个客户端实例在泄漏)、cmd是subscribe或blpop且age很大的(长期占用)、latest字段是cmd=keys、hgetall之类的高危命令。我见过一次是某个定时任务每 10 秒起 50 个线程跑批量操作,跑完不归还,一天下来攒了几千个连接。

内存水位同样会导致这个报错,虽然路径绕一点:maxmemory打满且淘汰策略是noeviction时,所有写命令返回OOM command not allowed,应用侧如果没做好异常处理,可能一直重试、一直占着连接不放。看info memory里的used_memory_human和maxmemory_human,比值长期在 0.9 以上就该扩容或者调淘汰策略了。

3.3 慢命令才是隐藏最深的元凶

Redis 处理命令是单线程的(命令执行部分),一条耗时 300ms 的命令会让这 300ms 内所有连接上的请求一起排队。客户端侧看到的现象就是"连接都借出去了,一个个卡在那里不动",池子迅速被占满,然后PoolException爆炸。而这时候connected_clients可能远没到上限,内存也很健康,非常容易误判成"池子太小"。

定位慢命令靠两个东西:

redis-cli config set slowlog-log-slower-than 10000 # 记录超过 10ms 的命令 redis-cli slowlog get 20 redis-cli slowlog len # 按累计耗时排序看命令画像 redis-cli info commandstats | sort -t= -k2 -rn | head -20

slowlog看单次,commandstats看累计。我喜欢用commandstats里的usec_per_call找出平均耗时高的命令类型,再和业务代码对照。常见的慢命令就那么几个:keys *(全量扫描,绝对禁止)、hgetall拉超大 hash、smembers拉几万元素的 set、zrange ... withscores拉大 zset、mget里塞几百个 key、还有flushdb/flushall这种核弹。另一个隐藏杀手是大 key 删除:删一个几百万元素的集合,Redis 4.0 之前是同步删的,直接阻塞好几秒,4.0 之后可以用unlink异步删。

我的处理顺序是:先把slowlog里最耗时的命令对应的业务调用点找出来,用scan替换keys,用hscan/sscan分批拉,大 key 拆成多个小 key,删除改用unlink。这一步做完,往往不用动任何池子参数,PoolException自己就消失了。

3.4 客户端侧最常见的是连接没归还

如果服务端一切正常、命令也不慢,那基本就是客户端在泄漏连接。Jedis 的手动用法必须严格配对:

// 错误写法:异常路径下连接永远不归还 Jedis jedis = jedisPool.getResource(); jedis.set("k", "v"); jedis.close(); // 上面抛异常,这行不执行 // 正确写法 Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.set("k", "v"); } catch (Exception e) { log.error("redis op failed", e); } finally { if (jedis != null) { jedis.close(); // 注意是 close,不是 shutdown } }

用RedisTemplate的话,Spring Data Redis 内部通过RedisConnectionUtils自动获取和释放连接,正常用redisTemplate.opsForValue().get()不需要操心归还。但如果你绕过RedisTemplate直接拿RedisConnection:

RedisConnection conn = connectionFactory.getConnection(); // 忘记 conn.close() —— 每个请求泄漏一个连接

那就等着池子被抽干。另外用RedisTemplate.execute(RedisCallback)时,回调里不要自己再去getConnection(),会嵌套借连接,容易死锁。

还有一个隐蔽点:RedisTemplate的executePipelined、executeWithStickyConnection这类方法会长时间持有连接,如果在里面做了耗时操作(比如循环发几万条命令),连接会被占用很久,池子很容易被打满。

4. 几个高频场景的完整复盘

4.1 场景一:慢命令把池子拖死

现象是流量高峰期必现PoolException,低峰期完全没有,connected_clients只有几百,内存正常。slowlog一拉,发现有个hgetall平均 120ms,对应的 key 是个攒了两年的用户行为 hash,元素超过 20 万。

修法分三步。第一,业务侧改成hscan分批读,每次 500 个 field,循环拉完;第二,把这个大 hash 按时间维度拆成user:behavior:202401、user:behavior:202402这样的多个小 key,读的时候只取需要的月份;第三,加个兜底,在代码里对单次 Redis 调用加超时熔断,超过 50ms 直接降级走本地缓存。

改完之后slowlog里最慢的命令从 120ms 降到 2ms,池子maxTotal=32就够用了,压根不用加到 200。这个案例我的体会是:池子不够用,八成是有人在池子里泡澡。

4.2 场景二:maxWait 设成 -1 把问题藏了三个月

有个服务maxWait写的是-1,也就是无限等待。问题不显性,只是高峰期接口 RT 慢慢涨。等到某天 Redis 网络抖了一下,所有线程都卡在借连接上,Tomcat 线程池被占满,整个服务不可用,日志里才刷出PoolException。

把maxWait改成 1000ms 之后,问题立刻显性化:高峰期能看到少量借连接超时的告警。顺着这条线索查,发现是某个接口在循环里单个set了 2000 次,每次借一次连接。改成 pipeline 批量提交后,连接占用时间从 40ms 降到 1ms,告警消失。

maxWait=-1最大的问题是把"池子不够"变成了"线程堆积",前者你能收到明确告警,后者只会表现为 RT 缓慢劣化,查起来非常痛苦。所以我坚持一句话:maxWait必须有限值,宁可快速失败,不要安静排队。

4.3 场景三:主从切换后池子里全是死连接

用哨兵或者集群模式的项目,运维做了一次主从切换,之后应用端持续报PoolException,PING又是通的。这种情况通常是池子里残存着指向旧主节点的连接,Sentinel 通知到位了但连接池里的连接还没被清理。

Jedis 哨兵模式要确保JedisConnectionFactory配置了正确的哨兵节点,并且池子的testOnBorrow或者testWhileIdle打开,让探活机制把死连接剔掉。Lettuce 侧有专门的拓扑刷新配置:

spring: data: redis: lettuce: cluster: refresh: adaptive: true period: 30s

集群模式下遇到MOVED重定向如果客户端没跟上,也会表现为操作超时进而耗尽池子,adaptive刷新可以在拓扑变化后主动更新连接。另外切换瞬间的重试策略要对,RedisTemplate默认重试次数是 0,可以配合 Resilience4j 或 Spring Retry 做一次短重试,但重试次数别超过 2 次,否则会把 Redis 压得更狠。

4.4 场景四:四个超时配置互相打架

这是最容易搞混的地方。客户端侧同时存在四个"超时",各管各的,配错了现象很奇怪:

配置项作用范围典型值配错的后果
connect-timeoutTCP 建连阶段500ms~1s太小则在网络抖动时批量建连失败;太大则借连接线程卡住
timeout(命令超时)单条命令读写1s~2s太小则大 key 读取频繁超时;太大则连接长期被占用
max-wait从池中借连接的等待时间500ms~1s设为 -1 导致线程无限堆积;设为 0 则一借不到就抛异常
socket keepalive / 服务端 timeout空闲连接是否被服务端断开服务端timeout 300服务端主动断连,池中连接变死连接,需探活机制清理

一个常见错误组合:connect-timeout设 5s,max-wait设 200ms。结果就是建连慢的时候,池子里的线程还没等到连接建立完成就超时抛异常,白白浪费了建连开销,还会不断重试把连接数推高。合理的搭配是max-wait ≥ connect-timeout,或者干脆在应用启动时预热minIdle个连接,避免运行时批量建连。

服务端的timeout配置也要看一眼。如果 Redis 配了timeout 300(空闲 300 秒断开),而客户端池子的空闲检测间隔是 10 分钟,那中间这 7 分钟池子里就是一堆死连接,借出来必报错。解决办法是把客户端time-between-eviction-runs调到小于服务端 timeout,并开启test-while-idle。

5. 改完之后怎么验证,怎么长期盯住

5.1 三个层面的关键指标

改完配置不算完,得确认真的好了。我一般从三个层面看指标。

JVM 侧,Spring Boot 通过 Micrometer 会暴露连接池指标(开启management.metrics.enable.jedis=true或lettuce),重点看active(正在被借用的连接数)、idle、waiters(等待借连接的线程数)。waiters长期大于 0 说明池子偏小或者有慢命令;active长时间贴顶maxTotal说明容量真的不够。这三个指标比看异常日志提前得多,异常是结果,waiters是前兆。

Redis 服务端侧,重点盯connected_clients、rejected_connections、blocked_clients、instantaneous_ops_per_sec,以及info commandstats里 top 命令的usec_per_call。我习惯把usec_per_call排前 5 的命令做成一张周报,一旦有命令的平均耗时涨了 50%,立刻去看对应业务代码有没有改动。

应用侧,统计PoolException的发生次数和时间分布。这个最好做成按分钟聚合的曲线,如果只在每天某个固定时间点出现,基本能锁定是定时任务;如果跟着流量曲线走,那多半是容量或者慢命令问题。

5.2 用压测把问题复现出来

线上问题难复现,就造一个。我常用的组合是:本地起一个和线上同版本的 Redis(Docker 一条命令就行),用redis-benchmark打满基础压力,同时用 JMeter 或 wrk 打应用接口。要验证连接池行为,关键是制造网络抖动:

# 给 Redis 端口加 100ms 延迟和 5% 丢包,模拟跨机房抖动 tc qdisc add dev eth0 root netem delay 100ms loss 5% # 验证完记得删掉规则,否则后面的测试全废 tc qdisc del dev eth0 root

在这个前提下观察waiters曲线和PoolException的数量,就能判断当前maxTotal、maxWait的组合在异常网络下是否扛得住。我的经验是:在 100ms 延迟下还能不抛PoolException的配置,线上基本就稳了。反过来,如果本地网络完美的情况下压测就报错,那说明池子容量严重不足,先加容量再谈别的。

5.3 告警阈值怎么定

别只配一条"出现 PoolException 就告警",那样你会在小抖动里疲于奔命。分两级:

  • 预警级:waiters连续 1 分钟大于maxTotal的 10%,或者active连续 5 分钟超过maxTotal的 80%。这时候还没报错,但趋势不对,可以提前看。
  • 告警级:PoolException每分钟超过 5 次,或者rejected_connections出现增长,或者 P99 接口耗时翻倍。

再补一条服务端的:connected_clients / maxclients > 0.8持续 3 分钟告警。这条能提前发现连接泄漏,比等到池子报错早得多。

6. 常见问题速查表与我的避坑清单

6.1 一张表覆盖八成报错

现象最可能原因快速验证方式处理动作
服务启动就报,一直复现地址/端口/密码错,或安全组不通redis-cli ping手动连修配置或开白名单
高峰期必现,低峰正常连接池容量不足或慢命令看waiters、slowlog提maxTotal,同时查慢命令
突然全量报错,之前很稳服务端 maxclients 打满info clients临时提maxclients,查泄漏源
只有部分实例报错网络分区或某台机器配置漏改对比实例配置和网络统一配置
报错伴随 RT 缓慢上涨maxWait=-1静默排队检查max-wait配置改成有限值
主从切换后开始报错池中死连接未清理test-while-idle是否开启开探活,配 Lettuce 拓扑刷新
偶发单次,无规律网络抖动 + 建连超时太短抓包看建连耗时应用启动预热 minIdle 连接
报错同时内存告警maxmemory 打满写拒绝info memory扩容或调淘汰策略

这张表我贴在工位上,遇到报错先扫一遍,通常两分钟内就能定方向。

6.2 几条用血换来的注意事项

第一,永远不要在业务代码里jedisPool.getResource()之后不 close。哪怕你觉得自己写对了,也建议在 code review 里专门加一条检查项,或者干脆封一个工具类统一处理。我抓过最夸张的一次是一个 try 里借了连接,catch 里 log 完就 return 了,一个月泄漏了几万个连接。

第二,maxTotal不是越大越好。加到几百之后,Redis 服务端处理连接本身的 CPU 开销会明显上升,尤其是短连接频繁创建销毁的场景。而且连接数堆上去之后,一旦某条慢命令出现,被阻塞的连接数也更多,雪崩规模更大。我见过把maxTotal从 32 加到 500 之后,报错频率反而上升的案例。

第三,把 Redis 调用包一层超时和降级。不管连接池配得多好,网络总有抽风的时候。给关键业务加一个本地缓存兜底,或者用 Resilience4j 做熔断,能在 Redis 抖动时保证主流程可用。这比调参数管用得多。

第四,别在生产环境用keys *排查问题。我见过同事为了找一个 key,在线上执行keys user:*,直接触发了一次 30 秒的阻塞,PoolException瞬间刷屏。正确做法是用scan游标分批遍历,或者用可视化管理工具按前缀过滤。顺带说一句,可视化工具有不少选择,但连生产库的时候一定要用只读账号,别手滑点了个删除。

第五,配置变更要能回滚。连接池参数调大容易,调小之后如果发现撑不住,回滚窗口是很短的。建议每次调参只改一个变量,观察 24 小时再动下一个,同时把变更记录写清楚。

第六,版本差异要留意。Spring Boot 2.4 前后配置前缀不同,commons-pool2的版本会影响空闲检测的行为,Jedis 3.x 和 4.x 的默认超时值也不一样。升级依赖的时候,顺手把这些参数在启动日志里打一遍对比,能避免很多"升级完就出问题"的莫名现象。

6.3 一个可以复制的排查脚本

最后贴一个我常用的小脚本,把前面那些命令串起来,出问题的时候一把梭:

#!/bin/bash # redis-triage.sh 用法: ./redis-triage.sh 10.0.0.21 6379 HOST=${1:-127.0.0.1} PORT=${2:-6379} CLI="redis-cli -h $HOST -p $PORT" echo "=== 1. 连通性 ===" $CLI ping || { echo "PING 失败,先查网络"; exit 1; } echo "=== 2. 连接数 ===" $CLI info clients | grep -E "connected_clients|blocked_clients|maxclients" echo "=== 3. 连接拒绝统计 ===" $CLI info stats | grep -E "rejected_connections|total_connections_received" echo "=== 4. 内存水位 ===" $CLI info memory | grep -E "used_memory_human|maxmemory_human|maxmemory_policy" echo "=== 5. 慢查询最近 10 条 ===" $CLI slowlog get 10 echo "=== 6. 命令耗时画像 ===" $CLI info commandstats | tr -d '\r' | awk -F'[:,=]' '/cmdstat/ {print $4, $0}' | sort -rn | head -10 echo "=== 7. 连接来源 TOP ===" $CLI client list | awk '{print $2}' | cut -d= -f2 | cut -d: -f1 | sort | uniq -c | sort -rn | head -10

跑一遍基本能把方向定下来。第 7 项特别有用,如果某个 IP 的连接数远超其他,那泄漏源就找到了。这个脚本我在好几家公司都用过,稍微改改就能接进运维平台的巡检任务里。

我个人在这类问题上的体会是:PoolException从来不是孤立的配置问题,它更像一个"症状",真正的原因往往藏在慢命令、连接泄漏、网络抖动或者拓扑变更里。上来就调maxTotal是最省事也最容易做错的选择,正确的顺序永远是先看异常尾巴,再排除池外因素,最后才动池子参数。还有一个习惯值得养成——任何一次 Redis 相关的故障处理完之后,都把slowlog和client list的当时快照存下来,过两周再对比一次,往往能发现一些慢性的、正在恶化但还没爆发的隐患,这比事后救火有价值得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 7:54:41

YOLOv11客流统计系统实战:从目标检测到热力图全解析

简介:面向零售业智能化升级的开发者和技术人员,这份实战文档系统讲解基于YOLOv11的多目标跟踪与热力图生成技术,直击客流统计中对检测效率、复杂场景准确率和可视化决策的核心需求。压缩包内包含1个PDF文件,大小2.03MB&#xff0c…

作者头像 李华
网站建设 2026/9/30 7:54:21

数据集格式怎么选?从CSV到TFRecord的工程化实践指南

平时做数据相关的工作,打交道最多的就是数据集和数据集格式。刚开始入行时,我拿到一份数据就习惯性用 pd.read_csv() 一把梭,不管它是图像、文本还是传感器数据。后来踩了不少坑,才意识到数据集格式这个东西,往小里说…

作者头像 李华
网站建设 2026/9/30 7:54:19

Git实战指南:分布式版本控制的原理、操作与团队协作

Git这个工具,我在一线写了十几年代码,从一开始觉得“多此一举”,到后来彻底离不开它,中间的转变过程还挺值得聊聊。Git不是某个公司出的某个网盘,也不是“代码备份工具”这么简单,它是一套分布式版本管理系…

作者头像 李华
网站建设 2026/9/30 7:54:07

机器视觉缺陷检测全链路实战:光源、相机、镜头选型与算法落地

简介:这份PDF资料面向从事机器视觉与图像处理的工程师、算法开发者及工业检测相关技术人员,系统梳理了工业缺陷检测中的核心知识体系。内容围绕硬件选型展开,涵盖光源类型与照明方式的选择(如背向照明、前向照明、同轴光、低角度光…

作者头像 李华
网站建设 2026/9/30 7:54:05

尤文图斯网上商城Java Web开发实战:JSP+Servlet+MySQL全解析

1. 项目先拆明白:这个“尤文图斯网上商城”到底在做什么 1.1 选题背景与需求场景还原 先说结论:jspm尤文图斯足球俱乐部网上商城系统,是一个典型的电商类Java Web毕业设计项目。标题里的“jspm”不是某个高深框架,而是这类老牌毕…

作者头像 李华
网站建设 2026/9/30 7:53:39

基于Django和协同过滤的电影推荐系统

1.毕业设计(论文)课题来源及内容:(注明自拟、科研、科技服务类别及任务提出单位) 课题来源:自拟课题内容:随着我们国家的互联网规模不断的扩大,用户规模越来越庞大&#…

作者头像 李华