Redis 连接池耗尽的一次异常定位
转载请注明出处:
最近在项目中遇到一个奇怪的现象,项目运行环境中的redis在业务运行中,一直没有更新redis的值,在服务的日志中也没有看到相关的异常,导致服务看起来正常,但和redis相关的功能却没有更新。记录下这个异常定位解决的过程。
登录到redis里面,发现redis也是运行正常的,且能正常获取。所以进入到了服务端里面,获取jvm线程进行具体分析,看到有很多个线程栈如下:

定位分析过程
pool-4-thread-1:
线程名称,表明该线程属于线程池pool-4的第一个工作线程(线程池通常由ThreadPoolExecutor管理)。Id=211:
JVM 内部分配的线程唯一标识符(非操作系统线程ID)。CPU 时间统计
cpu=692644037380 ns usr=644020000000 ns
cpu=692644037380 ns:
线程从启动至今消耗的 总 CPU 时间(包括内核态和用户态),单位为纳秒(≈ 692.64 秒)。usr=644020000000 ns:
线程在 用户态(User Mode) 消耗的 CPU 时间(≈ 644.02 秒)。
差值意义:cpu - usr ≈ 48.62秒为线程在内核态(Kernel Mode)的耗时,通常由系统调用(如 I/O、锁竞争)引起。
线程阻塞与等待统计
blocked 2294 for -1 ms waited 28442 for -1 ms
blocked 2294:
线程因 竞争锁(synchronized) 而被阻塞的次数(总计 2294 次)。for -1 ms:
阻塞时间的统计方式,-1 ms表示未记录具体阻塞时长(需启用 JVM 参数-XX:+PrintBlocked获取)。waited 28442:
线程在 等待条件触发(如Object.wait()或Condition.await())的次数(总计 28442 次)。for -1 ms:
等待时间的统计方式,-1 ms表示未记录具体等待时长(需启用-XX:+PrintWait获取)。
线程状态与堆栈跟踪
java.lang.Thread.State: WAITING
at sun.misc.Unsafe.park(Native Method)
- waiting on (a java.util.concurrent.ThreadPoolExecutor$Worker@5cf37a65)Thread.State: WAITING:
线程处于 无限期等待 状态,通常由以下操作触发:Object.wait()(无超时参数)。LockSupport.park()。Condition.await()(无超时参数)。
sun.misc.Unsafe.park(Native Method):
线程通过LockSupport.park()进入阻塞状态,底层调用Unsafe.park()。waiting on (a java.util.concurrent.ThreadPoolExecutor$Worker@5cf37a65):
线程正在等待ThreadPoolExecutor.Worker对象(线程池工作线程的封装)关联的条件变量(如任务队列非空)。
关键堆栈分析
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:750)核心路径:
线程从
LinkedBlockingQueue.take()尝试获取任务。若队列为空,调用
ConditionObject.await()进入等待。最终通过
LockSupport.park()挂起线程,直到新任务到达。
性能问题诊断
1. 高
waited次数(28442 次)可能原因:
线程池任务队列长期为空,工作线程频繁等待新任务。
任务生产速度不足(如上游系统吞吐量低)。
线程池配置不合理(核心线程数过多,超出实际需求)。
2. 高
blocked次数(2294 次)可能原因:
线程池内部锁竞争(如
Worker线程争用任务队列)。共享资源(如数据库连接池)的同步访问冲突。
3. CPU 时间分配
用户态耗时占比:
usr / cpu ≈ 644.02 / 692.64 ≈ 93%,表明线程主要执行用户代码,而非系统调用。若应用为计算密集型,此比例为正常现象。
优化建议
1. 线程池配置优化
调整核心线程数:
若队列长期为空,减少corePoolSize,避免线程闲置。new ThreadPoolExecutor(
corePoolSize, // 根据负载动态调整(如使用动态线程池框架)
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(capacity)
);
2. 任务队列监控
检查队列容量:
若使用无界队列(如LinkedBlockingQueue未指定容量),可能导致内存溢出,建议改为有界队列。监控队列堆积:
通过 JMX 或ThreadPoolExecutor的getQueue().size()实时观察任务积压情况。
3. 减少锁竞争
使用无锁数据结构:
替换LinkedBlockingQueue为ConcurrentLinkedQueue(需配合非阻塞任务调度逻辑)。分离读写操作:
若共享资源访问频繁,使用读写锁(ReentrantReadWriteLock)替代独占锁。
问题解决:
根据截图中的线程栈调用过程,可以定位到项目代码执行调用的地方,发现调用的地方是频繁批量更新redis缓存值得,且每次都是单独一条设置更新得。因此很快推测出来,是这个调用得地方在频繁更新redis缓存值时,导致服务中redis得连接数不够了,因此将代码中更新redis值得方式,使用管道得方式进行更新设置,问题得以解决。
Redis 连接池耗尽的一次异常定位的更多相关文章
- redis连接池 jedis-2.9.0.jar+commons-pool2-2.4.2.jar
java使用Redis连接池 jar包为 jedis-2.9.0.jar+commons-pool2-2.4.2.jar jar下载地址 package com.test; import redis ...
- Redis 连接池的问题
目录 Redis 连接池的问题 1 1. 前言 1 2.解决方法 1 前言 问题描述:Redis跑了一段时间之后,出现了以下异常. Redis Timeout ex ...
- 三:Redis连接池、JedisPool详解、Redisi分布式
单机模式: package com.ljq.utils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; ...
- Redis连接池
package com.lee.utils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; impor ...
- Redis】Java中使用Jedis操作Redis(Maven导入包)、创建Redis连接池
如果我们使用Java操作Redis, 需要确保已经安装了 redis 服务及 Java redis 驱动. Maven项目可以直接在pom.xml中加入jedis包驱动: <!-- https: ...
- Java Redis 连接池 Jedis 工具类
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.Jedis; import re ...
- Swoole Redis 连接池的实现
概述 这是关于 Swoole 入门学习的第九篇文章:Swoole Redis 连接池的实现. 第八篇:Swoole MySQL 连接池的实现 第七篇:Swoole RPC 的实现 第六篇:Swoole ...
- redis连接池(JedisPool)资源归还及timeout详解
转载. https://blog.csdn.net/yaomingyang/article/details/79043019 一.连接池资源类详解都在注释上 package redis.v1.clie ...
- 红眼技术博客 » redis连接池红眼技术博客 » redis连接池
红眼技术博客 » redis连接池 redis连接池
- redis连接池操作
/** * @类描述 redis 工具 * @功能名 POJO * @author zxf * @date 2014年11月25日 */public final class RedisUtil { p ...
随机推荐
- 《基于NLP内容理解》出书
<基于NLP内容理解>出书初心: 1)情怀&梦想:记得自己高中的时候每次冲进我们县城的书店,都能看到书店中央摆放着白岩松老师的一本自传,当时的那种崇拜一直萦绕在自己的心里,想着自己 ...
- ruoyi-vue axios通过接口请求wav、mp3音频二进制数据
实现方式 在axios请求中注明responseType: 'blob',headers的Accept: 'audio/wav'不清楚要不要写,我加上了(请求接口) 在接口返回值中,使用Blob的构造 ...
- autMan奥特曼机器人-安装或更新golang依赖
autMan2.3.4及以上需要更新中间件或安装golang依赖,参照下列步骤: 一.直装版本 ssh下进入autMan文件夹下plugin/scripts下面输入以下指令: go get -u gi ...
- 异地组网工具 Radmin_LAN:将远程计算机连接到单一本地网络
Radmin LAN是一款使用简单的免费软件产品,用于创建虚拟本地网络.该程序允许用户安全地连接位于防火墙后的计算机.为流量提供一个安全通道.可靠的端到端加密(256位AES)确保您的连接安全. Ra ...
- . net core 项目创建和发布
1.发布和部署到IIS https://www.cnblogs.com/loverwangshan/p/11284169.html https://www.cnblogs.com/wangjiegua ...
- 【刚度矩阵推导】2d frame 单元
2d frame 单元是x-y平面上的单元,每个节点上有2个平移自由度的和一个转动自由度.局部坐标系下,单元位移向量为: \( u=[ u_1 ,u_2 ,u_3, u_4, u_5, u_6]^{T ...
- python 二级 语言基本元素笔记-字符串
l='12345' 1.递增顺序: 正向递增从0开始 负向从[-1]开始,l[1]=2,l[-1]=5 2.切片操作:左开右闭,l[2:5]=3,4 3.导入库,引入 库名.函数名 input函数 输 ...
- [SWPUCTF 2021 新生赛]ez_unserialize
概括 这是一道PHP反序列化的CTF赛题,本意是想用这道题对PHP反序列化进行一定的学习. 过程 我们打开赛题,看看内容 没有发现什么东西,看看他的页面代码 根据他的提示,感觉是存在一个robots. ...
- 错误修正记录:synopsys license服务器激活报错-No valid hostids,exiting
长久没用vcs,此次新装design compiler,再次用到lmgrd,报错 (snpslmd)No valid hostids,exiting (snpslmd)EXITING DUE TO S ...
- Docker 镜像及容器命令
授人以鱼不如授人以渔,先列出官方命令文档 docker命令文档地址:https://docs.docker.com/engine/reference/commandline/images/ 镜像相关指 ...