一次mysql数据库连接池泄露的解决经历

举报
经典鸡翅 发表于 2022/02/17 22:13:16 2022/02/17
【摘要】 前言 最近用了公司某框架,部署到现场后,现场运维开始维护现场数据,在不断操作的过程中,系统崩溃,查看后台日志,druid连接池已经获取不到连接。于是开始了排查之旅。在此记录。 排查开始 首先后台的报...

前言

最近用了公司某框架,部署到现场后,现场运维开始维护现场数据,在不断操作的过程中,系统崩溃,查看后台日志,druid连接池已经获取不到连接。于是开始了排查之旅。在此记录。

排查开始

首先后台的报错是这样的。

exception=org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 10, maxActive 10

  
 
  • 1
  • 2

第一反应maxActive设置的数量太少了。于是改为100。重新启动,并再次操作大量数据。发现过了一段时间100个也满了。

此时问题不简单了。看来是有代码用了程序连接后,没有释放。

接下来开始确认原因到底是不是有没有释放。

在项目中使用的druid连接池。druid连接池是自带图形化监控工具的。于是开始在项目中配置,启动druid连接池。

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DruidConfig {

   @Bean
   public ServletRegistrationBean statViewServlet(){
      ServletRegistrationBean srb =
         new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
      //设置控制台管理用户
      srb.addInitParameter("loginUsername","root");
      srb.addInitParameter("loginPassword","root");
      //是否可以重置数据
      srb.addInitParameter("resetEnable","false");
      return srb;
   }

   /**
    * 注册FilterRegistrationBean
    * @return
    */
   @Bean
   public FilterRegistrationBean druidStatFilter() {
      FilterRegistrationBean bean = new FilterRegistrationBean(new WebStatFilter());
      //添加过滤规则.
      bean.addUrlPatterns("/*");
      //添加不需要忽略的格式信息.
      bean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
      return bean;
   }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

重新启动后,访问:localhost:8081/druid。这个路径有人大概会怀疑如果我是通过网关管理的微服务框架,需要通过网关转发访问吗?其实大可不必,通过网关也可以,也是跳转到直接访问的地址。

进入访问地址,跳转到如下的登录界面。

输入刚才在代码中配置的用户名和密码,则可以成功进入。

我们此时要点击数据源,去关注建立的逻辑连接数和关闭的逻辑连接数,关键的指标在这里,如果连接池的开启和关闭是正常的,那么二者的值应该是相等的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fV4YvQ0f-1598265593001)(https://img2020.cnblogs.com/blog/1534147/202008/1534147-20200822162545047-326949079.png)]

再看看此时的活跃连接数

为0,此时正常,因为还没有进行操作。

接下来开始对之前的操作进行复现,锁定具体的操作。重复之前现场运维所做操作。

通过不断的点击功能,缩小功能范围,最终发现,只要点击左侧树,就会造成逻辑打开连接和逻辑关闭次数不一致。

活跃连接数也到了二者之差。等了几分钟,仍然是这个情况。那么实锤了 这里的代码有问题,连接应该没有释放。那么代码那么多,该如何发现具体代码的位置呢。

接下来配置druid的abandon策略。通过abandon可以强制回收数据库的连接。而活跃的连接被回收则会打印堆栈信息,这是就知道是哪里的sql代码没有释放了。

配置如下:

spring:
  datasource:
    druid:
      remove-abandoned: true
      remove-abandoned-timeout: 30
      log-abandoned: true

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

重启项目,这个时候durid的监控活跃连接数的功能就可以看到代码信息。

我们点击如下位置:

就会弹出上图的堆栈信息。打马赛克的地方就是代码的详细位置,会标记出来。开发人员去响应的类找到相应代码查看即可。

经过查看代码发现,代码的连接释放存在问题。是自己封装的sql查询类。改为mybatis的写法后,问题解决。

文章来源: blog.csdn.net,作者:经典鸡翅,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/hanqing456/article/details/108205030

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。