转载请注明出处:

  最近在项目中遇到一个奇怪的现象,项目运行环境中的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)
      • 核心路径

        1. 线程从 LinkedBlockingQueue.take() 尝试获取任务。

        2. 若队列为空,调用 ConditionObject.await() 进入等待。

        3. 最终通过 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 连接池耗尽的一次异常定位的更多相关文章

  1. 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 ...

  2. Redis 连接池的问题

      目录 Redis 连接池的问题    1 1.    前言    1 2.解决方法    1     前言 问题描述:Redis跑了一段时间之后,出现了以下异常. Redis Timeout ex ...

  3. 三:Redis连接池、JedisPool详解、Redisi分布式

    单机模式: package com.ljq.utils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; ...

  4. Redis连接池

    package com.lee.utils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; impor ...

  5. Redis】Java中使用Jedis操作Redis(Maven导入包)、创建Redis连接池

    如果我们使用Java操作Redis, 需要确保已经安装了 redis 服务及 Java redis 驱动. Maven项目可以直接在pom.xml中加入jedis包驱动: <!-- https: ...

  6. Java Redis 连接池 Jedis 工具类

    import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.Jedis; import re ...

  7. Swoole Redis 连接池的实现

    概述 这是关于 Swoole 入门学习的第九篇文章:Swoole Redis 连接池的实现. 第八篇:Swoole MySQL 连接池的实现 第七篇:Swoole RPC 的实现 第六篇:Swoole ...

  8. redis连接池(JedisPool)资源归还及timeout详解

    转载. https://blog.csdn.net/yaomingyang/article/details/79043019 一.连接池资源类详解都在注释上 package redis.v1.clie ...

  9. 红眼技术博客 » redis连接池红眼技术博客 » redis连接池

    红眼技术博客 » redis连接池 redis连接池

  10. redis连接池操作

    /** * @类描述 redis 工具 * @功能名 POJO * @author zxf * @date 2014年11月25日 */public final class RedisUtil { p ...

随机推荐

  1. [BZOJ3514] [Codechef MARCH14] GERALD07加强版 题解

    名字感觉挺奇怪的. 考虑离线算法.首先答案就是用 \(n\) 减去连完边后的生成树森林边数.生成树当然就可以用 \(lct\) 求解了.我是不会告诉你这个时候我已经开始想回滚莫队了的. 考虑当我们倒序 ...

  2. 使用 Visual Paradigm 的业务流程模型和符号 (BPMN) 综合指南

    业务流程模型和符号 (BPMN) 是一种用于建模和记录业务流程的标准化图形符号.它被广泛采用,因为它能够提供一种清晰.通用的语言,所有利益相关者(业务分析师.技术开发人员和管理人员)都能理解.Visu ...

  3. Hive - [06] 行转列,列转行

    行转列(多行转一行) 1.创建表,并插入示例数据. create table students_info( `SNO` string comment '学生编号', `name` string com ...

  4. ETL工程师

    Python Flume DataX HDFS 数仓建模分层:ODS.DIM.DWD.DWS.APS Kettle.Informatica SQL(Oracle.MySQL)

  5. 算子var_threshold

    算子var_threshold 名称 var_threshold - 通过局部均值和标准差分析对图像进行阈值处理. 签名 var_threshold(Image : Region : MaskWidt ...

  6. .NET 10首个预览版发布:重大改进与新特性概览!

    前言 .NET 团队于2025年2月25日发布博文,宣布推出 .NET 10 首个预览版更新,重点改进.NET Runtime.SDK.Libraries .C#.ASP.NET Core.Blazo ...

  7. win7系统清理C盘空间方法实测

    问题描述:win7电脑C盘容易满,采用如下方法清理 方法一:win+r,输入%temp%查看临时文件,手动删除不需要的文件 方法二:减小休眠文件:如果你很少使用休眠模式,可以通过win+r输入cmd命 ...

  8. 【自荐】一款简洁、开源的在线白板工具 Drawnix

    在线白板工具 Drawnix -- 名字源于绘画(Draw)与凤凰(Phoenix)的灵感交织. Drawnix 的定位是一个开箱即用.开源.免费的在线白板工具产品, 集思维导图.流程图.画笔于一体, ...

  9. class com.ttsx.activity.item.dao.entity.RoleMenu ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method. 报错解决办法

    启动项目的时候,有几条WARN警告,如图: 引起原因: 是因为数据表实体类 没有di导致的. 例如: 解决办法: 增加实体id字段!或可以忽略!不影响程序!

  10. 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构

    概述 先看下面的图片,我们去旅游选择出行模式有很多种,可以骑自行车.可以坐汽车.可以坐火车.可以坐飞机. 作为一个程序猿,开发需要选择一款开发工具,当然可以进行代码开发的工具有很多,可以选择Idea进 ...