当前位置:   article > 正文

druid连接池错误:pool.GetConnectionTimeoutException: wait millis 60000, active 20。。。_caused by: com.alibaba.druid.pool.getconnectiontim

caused by: com.alibaba.druid.pool.getconnectiontimeoutexception: wait millis

com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 20, maxActive 20, creating 0

活动的连接数为20, 最大的连接数为20, 活动的连接数与最大连接数相同,连接池用完了,在等待60秒后,没有新连接可用,然后超时了。

stat监控页面显示,活跃连接数很高不释放。CPU超过100%。

当程序存在缺陷时,申请的连接忘记关闭,这时候,就存在连接泄漏了。

比如Connection connection = jdbcTemplate.getDataSource().getConnection(); 这样得到的连接spring不会再帮你关闭,你需要手动关闭。

1、properties配置:

  1. #druid recycle Druid的连接回收机制
  2. #超过时间限制是否回收
  3. spring.datasource.druid.removeAbandoned = true
  4. #超时时间;单位为秒。180秒=3分钟
  5. spring.datasource.druid.removeAbandonedTimeout = 180
  6. #关闭abanded连接时输出错误日志
  7. spring.datasource.druid.logAbandoned = true


2、xml配置:

  1. <!-- 超过时间限制是否回收 -->  
  2. <property name="removeAbandoned" value="true" />  
  3. <!-- 超时时间;单位为秒。180秒=3分钟 -->  
  4. <property name="removeAbandonedTimeout" value="180" />  
  5. <!-- 关闭abanded连接时输出错误日志 -->  
  6. <property name="logAbandoned" value="true" />


此配置项会影响性能,只在排查的时候打开,系统运行时最好关闭。 

此项配日志会将连接泄漏位置打印出来,手动关闭泄露位置的连接就行了。

以下为错误示例就会造成连接泄漏:

  1. public class Test5 {
  2.     @Autowired
  3.     private JdbcTemplate jdbcTemplate;
  4.  
  5.     private void test() {
  6.         Connection connection = null;
  7.         PreparedStatement ps = null;
  8.         ResultSet rs = null;
  9.         try {
  10.             for (int i = 0; i < 2; i++) {
  11.                 connection = jdbcTemplate.getDataSource().getConnection();
  12.                 // 第一次使用connection
  13.                 ps = connection.prepareStatement("select 1 from dual");
  14.                 rs = ps.executeQuery();
  15.                 while (rs.next()) {
  16.                     System.out.println(rs.getString(1));
  17.                 }
  18.                 // 连接connection可以一直使用,避免频繁创建connection消耗资源
  19.                 // ps、rs记得要关闭
  20.                 rs.close();
  21.                 ps.close();
  22.  
  23.                 // 第二次使用connection
  24.                 ps = connection.prepareStatement("select 1 from dual");
  25.                 rs = ps.executeQuery();
  26.                 while (rs.next()) {
  27.                     System.out.println(rs.getString(1));
  28.                 }
  29.                 // 连接connection可以一直使用,避免频繁创建connection消耗资源
  30.                 // ps、rs记得要关闭
  31.                 rs.close();
  32.                 ps.close();
  33.  
  34.                 // 第三次使用connection
  35.                 ps = connection.prepareStatement("select 1 from dual");
  36.                 rs = ps.executeQuery();
  37.                 while (rs.next()) {
  38.                     System.out.println(rs.getString(1));
  39.                 }
  40.                 // 连接connection可以一直使用,避免频繁创建connection消耗资源
  41.                 // ps、rs记得要关闭
  42.                 rs.close();
  43.                 ps.close();
  44.             }
  45.         } catch (Exception e) {
  46.             e.printStackTrace();
  47.         } finally {
  48.             if (connection != null) {
  49.                 try {
  50.                     connection.close();
  51.                 } catch (SQLException e) {
  52.                     e.printStackTrace();
  53.                 }
  54.             }
  55.         }
  56.  
  57.     }
  58. }

粗略一看没什么问题,finally最终一定会关闭连接,但是仔细看连接在for循环中打开的,实际打开了2个连接只关闭了一个,最终每调用一次该方法就会泄漏一次连接。将connection = jdbcTemplate.getDataSource().getConnection();放在循环外面就解决了。

错误示例二:

  1. public class Test5 {
  2.     @Autowired
  3.     private JdbcTemplate jdbcTemplate;
  4.  
  5.     private void test() {
  6.         Connection connection = null;
  7.         PreparedStatement ps = null;
  8.         ResultSet rs = null;
  9.         try {
  10.             connection = jdbcTemplate.getDataSource().getConnection();
  11.             ps = connection.prepareStatement(sql);
  12.             rs = ps.executeQuery();
  13.             while (rs.next()) {
  14.                 if (rs.getInt(1) == 1) {                    
  15.                     test();// 递归
  16.                 }
  17.             }
  18.             rs.close();
  19.             ps.close();
  20.         } catch (Exception e) {
  21.             e.printStackTrace();
  22.         } finally {
  23.             if (connection != null) {
  24.                 try {
  25.                     connection.close();
  26.                 } catch (SQLException e) {
  27.                     e.printStackTrace();
  28.                 }
  29.             }
  30.         }
  31.  
  32.     }
  33. }


递归也可能会导致,连接数不够。当递归函数需要递归很多次,只有递归结束才开始关闭连接,这期间活跃连接数陡增,若果这个函数被并发调用更恐怖。正确的做法,将connection作为递归函数的参数传递进去。

  1. // isClosed()即使断开连接也会返回false,只有close()后才返回true。
  2.  // isValid(1)单位秒,连接失效返回false。
  3. if (connection == null || connection.isClosed() || !connection.isValid(1)) {
  4.         connection = jdbcTemplate.getDataSource().getConnection();
  5.  }

一般一个connection可以创建300个preparedstatement同时使用,一个connection最大并行299个preparedstatement,记住preparedstatement用完后要关闭,connection每创建一个preparedstatement就相当于打开一个游标,超过300个就会报connection打开游标超出最大数(ORA-01000: 超出打开游标的最大数)。每个线程的preparedstatement应该是并行运行的。

所以体现出来spring管理connection连接的好处,不用去关心连接是否已经手动关闭。当项目中大量使用手动维护connection连接时,成千上万代码就会难免忘记关闭connection造成泄漏。

druid 配置参考 : DruidDataSource配置 · alibaba/druid Wiki · GitHub阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池 - DruidDataSource配置 · alibaba/druid Wikihttps://github.com/alibaba/druid/wiki/druiddatasource%E9%85%8D%E7%BD%AE

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/337025
推荐阅读
相关标签
  

闽ICP备14008679号