您好,欢迎访问代理记账网站
移动应用 微信公众号 联系我们

咨询热线 -

电话 15988168888

联系客服
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

关于数据库连接池满了的问题详解

关于数据库连接池满了的问题详解

  • 代码级问题
  • 实例问题
  • 问题根源
  • 问题扩展

代码级问题

问题重现:
某某系统在生产环境使用一定时间后表现出用户不能登录,后台tomcat日志报如下错:

   2008-08-14 12:31:35,029 [org.hibernate.util.JDBCExceptionReporter]-[WARN] SQL Error: 0, SQLState: null
   2008-08-14 12:31:35,029 [org.hibernate.util.JDBCExceptionReporter]-[ERROR] Cannot get a connection, pool exhausted
   2008-08-14 12:31:35,029 [org.hibernate.util.JDBCExceptionReporter]-[WARN] SQL Error: 0, SQLState: null
   2008-08-14 12:31:35,029 [org.hibernate.util.JDBCExceptionReporter]-[ERROR] Cannot get a connection, pool exhausted

可以看到显然是连接池满了,重启服务器之后就可以正常使用了。但是因为我们的tomcat连接池的配置连接参数好像很大,所以应该肯定是程序出问题了。

因为系统之前已经修改过一次因为代码的错误而导致的数据库连接没有释放的问题,所以这一次的问题比较不好定位,不能知道是哪些操作的连接池没有释放。

后来利用脚本查看当前数据库(sybase)哪些连接没有被释放,其中的ip为tomcat的发布地址(因为数据库连接都是由tomcat发起):

declare cur_spid cursor 
for
select spid from sysprocesses where ipaddr='172.16.7.8'
go

declare @spid Integer
open  cur_spid
fetch cur_spid into @spid 
while @@sqlstatus=0
begin
        print '%1!' , @spid
  dbcc traceon(3604)
  dbcc sqltext(@spid )
  fetch cur_spid into @spid 
end
close cur_spid

使用该脚本之后,执行结果都是打印出大量类似的下面的三行:

184
DBCC execution completed. If DBCC printed error messages, contact a user with System Administrator (SA) role. 
SQL Text: set CHAINED off 

直接使用上面的脚本打印的结果是当前占用数据库连接池的spid(第一行),以及连接正在执行的sql(第三行)。

后来发现每登录一次系统,使用上面的脚本执行结果就会有一个连接没有被释放(一般连接会在一段时间之后释放),除非是重启tomcat否则一直占用。

跟踪登陆代码发现有如下的写法(调用存储过程):

Session session = this.getSession();
        Transaction tx = session.beginTransaction();
        Connection con = session.connection();
        try {          
                  //……  
            CallableStatement cstmt = con.prepareCall("{call K_TJ..PR_GET_AjCount(?,?,?,?,?,?,?,?,?,?,?,?) }");
            //…… 
            ResultSet resultSet = cstmt.executeQuery();
            tx.commit();
            if (resultSet.next())
                ajCount = resultSet.getInt(1);
            resultSet.close();
        } catch (Exception e) {
                tx.rollback();
            e.printStackTrace();
            return 0;
        } finally{
                try
            {
                con.close();
            }
            catch (SQLException e)
            {
                e.printStackTrace();
            }
        }

这里有几个问题:
一是把hibernate和connection的用法使用混乱了;
二是使用session获取的连接不需要自己关闭,应该关闭session(一个session对应一个connection),这里刚好用使用反了。

后来试着把con.close()改成session.close()问题就没有了,如果是使用spring提供的getSession()获取的连接,最好是使用releaseSession()方法进行释放。对于release的理解:“release不一定是关闭连接,就像连接池的连接一样。release只是放回池中,你要关闭了就不能放回池中了,而且直接close可能会抛异常,release不会抛异常,因为里边有对环境的判断”,把con.close()改成releaseSession()问题也解决了。

但是我们的项目中使用了spring,对存储过程调用最好是使用jdbcTemplate。退一步如果要获取一个connection,最好能使用Summer提供的jdbcDao获取,即jdbcdao.getDataSource().getConnection(),当然这样的连接完全就需要自己手工关闭了。

最后搜了一下代码,把程序中如上调用存储过程的地方全部改为使用jdbcTemplate问题解决。最终代码如下:

getJdbcDAO().getJdbcTemplate().execute(
                "{call K_TJ..PR_GET_AjCount(?,?,?,?,?,?,?,?,?,?,?,?) }",
                new CallableStatementCallback() {
                  public Object doInCallableStatement(CallableStatement cstmt)
                                throws SQLException, DataAccessException {
                        //……
                        ResultSet resultSet = cstmt.executeQuery();
                        if (resultSet.next())
                          return new Integer(resultSet.getInt(1));
                        // ……
                });


连接池的问题解决了。以上就是一个典型的在代码中就存在问题导致数据库连接池满了不得不重启服务器的问题。

实例问题

下面再看一个实例:
数据库连接池满—如何排查SQL2005性能问题
有个Asp.net 2.0 开发的网站,部署之后进行测试,发现测试次数不多的情况下就会提示“数据库连接池已满,无法获取连接”,系统完全奔溃!
如何入手去分析问题?
这个问题有两种可能:

1、程序员在建立数据库连接后,没有关闭连接,导致连接数直线上升。或者在关闭连接的语句前发生了异常,导致没有执行关闭操作,跟上面那个章节的一样的问题。
2、业务上要求的数据库连接数真的很多。(这个可能很小)

这个问题可以从下面几方面入手:

1、在运行系统的同时,在sql管理器中观察数据库连接情况或者从sql的字典表中观察。看看在运行哪些操作的时候,数据库连接数不断增加。
2、可以使用sql server profiler,观察连接事件。这个可能更直观些。
3、用一些其他的数据库诊断工具如I3 for sql server。

问题根源

这种问题的根源就是数据库连接数过多,连接没有得到释放。
如何去查找到底是哪些连接没有得到释放?难道只能一个个代码去看吗?另外,在服务器上是否有工具或命令查看当前应用数据库连接数是多少?或者能在服务器产生日志查看哪些代码执行没有释放连接?

首先当访问量大的时候,可以试试使用数据连接池连接,在tomcat中Context.xml中配置以下参数,应该可以解决这个问题。

maxActive:设置数据库连接池中活动状态连接的最大数目,为0则不受限制;
maxIdle:设置数据库连接池中空闲状态连接的最大数目,为0则不受限制;
maxWait:设置数据库连接池中空闲状态连接的最长等待时间,超时则抛出异常,为-1则可无限等待;

问题扩展

根据上一章节内容再看一个实例:
在这里插入图片描述

日志显示: INFO util.DBExecActQueueRepository: Jdbc max num is: 200, active num is arriving: 195 
原因是因为数据库的连接池满了

根据上一章节可以知道:
maxActive:最大连接数据库连接数,设置成 0 为没有限制
maxIdle:最大等待连接中的数量,设置成0 为没有限制
maxWait:最大等待毫秒数, 单位为 ms, 超过时间会出错误信息

一般把maxActive设置成可能的并发量就可以,这里是因为设置的数值为200,所以最多能有200个数据库连接,但是200在我当前的环境已经够用了,为什么连接池还是会满呢?

通常情况下,锁表也会导致数据库连接池满,这个时候就需要查看被锁的表了,查询Mysql 哪些表正在被锁状态。

方法1: show OPEN TABLES where In_use > 0;  
方法2: SHOW PROCESSLIST;
解锁锁定的表:  kill id;

分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进