关于数据库连接池满了的问题详解
关于数据库连接池满了的问题详解
代码级问题
问题重现:
某某系统在⽣产环境使⽤⼀定时间后表现出⽤户不能登录,后台tomcat⽇志报如下错:
2008-08-1412:31:35,029[org.hibernate.util.JDBCExceptionReporter]-[WARN] SQL Error:0, SQLState: null
2008-08-1412:31:35,029[org.hibernate.util.JDBCExceptionReporter]-[ERROR] Cannot get a connection, pool exhausted
2008-08-1412:31:35,029[org.hibernate.util.JDBCExceptionReporter]-[WARN] SQL Error:0, SQLState: null
2008-08-1412: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 =Session();
Transaction tx = session.beginTransaction();
Connection con = tion();
try{
/
/……
CallableStatement cstmt = con.prepareCall("{call K_TJ..PR_GET_AjCount(?,?,?,?,?,?,?,?,?,?,?,?) }");
//……
ResultSet resultSet = uteQuery();
txmit();
())
ajCount = Int(1);
resultSet.close();
}catch(Exception e){
e.printStackTrace();
return0;
}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获取,即DataSource().getConnection(),当然这样的连接完全就需要⾃⼰⼿⼯关闭了。
最后搜了⼀下代码,把程序中如上调⽤存储过程的地⽅全部改为使⽤jdbcTemplate问题解决。最终代码如下:
getJdbcDAO().getJdbcTemplate().execute(
"{call K_TJ..PR_GET_AjCount(?,?,?,?,?,?,?,?,?,?,?,?) }",
new CallableStatementCallback(){
public Object doInCallableStatement(CallableStatement cstmt)
throws SQLException, DataAccessException {
//……
ResultSet resultSet = uteQuery();
())
return new Int(1));
// ……
});
连接池的问题解决了。以上就是⼀个典型的在代码中就存在问题导致数据库连接池满了不得不重启服务器的问题。
实例问题
下⾯再看⼀个实例:
数据库连接池满—如何排查SQL2005性能问题
有个Asp 2.0 开发的⽹站,部署之后进⾏测试,发现测试次数不多的情况下就会提⽰“数据库连接池已满,⽆法获取连接”,系统完全奔溃!
如何⼊⼿去分析问题?
这个问题有两种可能:
1、程序员在建⽴数据库连接后,没有关闭连接,导致连接数直线上升。或者在关闭连接的语句前发⽣了异常,导致没有执⾏关闭操作,跟上⾯那个章节的⼀样的问题。
2、业务上要求的数据库连接数真的很多。(这个可能很⼩)
这个问题可以从下⾯⼏⽅⾯⼊⼿:
1、在运⾏系统的同时,在sql管理器中观察数据库连接情况或者从sql的字典表中观察。看看在运⾏哪些操作的时候,数据库连接数不断增加。
2、可以使⽤sql server profiler,观察连接事件。这个可能更直观些。
3、⽤⼀些其他的数据库诊断⼯具如I3 for sql server。
问题根源
这种问题的根源就是数据库连接数过多,连接没有得到释放。
如何去查到底是哪些连接没有得到释放?难道只能⼀个个代码去看吗?另外,在服务器上是否有⼯具或命令查看当前应⽤数据库连接数是多少?或者能在服务器产⽣⽇志查看哪些代码执⾏没有释放连接?
⾸先当访问量⼤的时候,可以试试使⽤数据连接池连接,在tomcat中l中配置以下参数,应该可以解决这个问题。
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; 
sql连接不上服务器⽅法2: SHOW PROCESSLIST;
解锁锁定的表: kill id;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。