处理 wait millis 60009, active 50 ,maxactive 200 异常 过程

处理 wait millis 60009, active 50 ,maxactive 200 异常 过程
2018年04月19日 16:48:46

先说明一下项目特点:

       业务需要,需要进行相当多的数据库查询和插入、更新操作。生产环境30万数据,本地为了开发方便,只准备了两万数据。
之前的开发过程中测试项目,启动项目后 执行sql操作正常,日志一直再打印hibernate的sql。过一段时间以后 ,就突然停止打印了,等待一段时间(就是wait millis 配置的大小)后,控制台日志打印了wait millis 60009, active 50 ,maxactive 200这句话 。当时第一次遇到这个问题,检查发现在spring的application.xml中配置了数据源druid。里面的配置有这几个参数。当时maxactive 是 50。意思很明显,活动的连接数与最大连接数相同,连接用完了,在等待新的连接,却没有新连接可用,然后超时了。当时我没有意识到这点,只意识到数据库连接不够用,而且网上也有说连接不够用的,就把maxactive 调大,就没有出现这个问题了。
近日,在开发环境部署后,发现数据量太大,这个问题又出现了,maxactive 调的越大,这个问题出现的越晚,但是肯定会出现,说明问题没有根本解决。之后我就详细了解下druid的配置参数都是什么意思,又参考此文:https://www.cnblogs.com/netcorner/p/4380949.html 。

【使用druid连接池的超时回收机制排查连接泄露问题  https://www.cnblogs.com/netcorner/p/4380949.html

在工程中使用了druid连接池,运行一段时间后系统出现异常:

Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection;

      nested exception is com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60009, active 50
      at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
      at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:280)
      ... 64 more
Caused by: com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 50
      at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1071)
      at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:898)
      at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4544)

mysql数据库最大连接数设置为500,使用客户端能正常连接。连接数被未被占满。

分析原因应该是程序中有地方连接未关闭造成的。那如何来定呢?使用druid连接池的超时回收机制,在配置中增加以下内容:

  
  
  
  
  
   

运行程序,当连接超过3分钟后会强制进行回收,并输出异常日志。

2014-10-13 16:02:28,919 ERROR [com.alibaba.druid.pool.DruidDataSource] -

open stackTrace at java.lang.Thread.getStackTrace(Thread.java:1567)
      at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:995)
      at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4544)
      at com.alibaba.druid.filter.stat.StatFilter.dataSource_getConnection(StatFilter.java:661)
      at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4540)
      at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:919)
      at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:911)
      at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:98)
      at cn.org.xxx.xxx.xxx.PaginationInterceptor.intercept(PaginationInterceptor.java:96)
      at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:60)
      at com.sun.proxy.$Proxy59.query(Unknown Source)
      at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:108)

很清楚地可以看到是在哪里打开的连接未关闭一直在占有。此配置项会影响性能,只在排查的时候打开。系统运行时最好关闭。

说白了,因为程序原因,导致无论数据库连接多大,都会用完,用完的数据库连接没有释放掉。

由于控制台日志没有异常位置,造成不易判断bug位置。
添加druid的配置:能够使异常在控制台显示出来,便于问题定位。

  
  
  
  
  
   

超过180s,强制回收数据库连接,回收的时候打印日志,日志中会显示代码中没有释放数据库连接的代码具体位置。
我把上述配置添加后,运行项目,果然在控制台出现异常日志:

 

处理 wait millis 60009, active 50 ,maxactive 200 异常 过程_第1张图片

注意最后两行显示的方法名。搜索日志,发现只要出现这个异常,都是这个方法 addRowGuid(),这就意味着代码有bug,这个方法每一次调用,都不会关闭数据库连接。
排查代码,发现此方法中位于dao中,内容很简单,就是用hibernate的save方法,往数据库插入一个字段值。this.save(xxx);
之前的代码用到这个save方法时,我都是手动提交事务的,只有这一处没有手动提交,于是更改代码添加手动提交事务,如下:

Session sess = this.getSession();
Transaction transaction=null;
try {
    transaction = sess.beginTransaction();
    sess.save(materialChange);
    transaction.commit();
}catch(Exception e) {
    logger.error(String.format("执行RowGuid=%s 存储数据到change表 失败 )", rowGuid));
    transaction.rollback();
}finally{
    sess.close();
}

而且我特意把maxActive参数降低改为200,远远小于之前的值,测试项目,此问题再也没有出现。

你可能感兴趣的:(20,问题,处理)