数据库连接池满的问题
XX系统在生产环境使用一定时间后表现出用户不能登录,后台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
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连接池的配置连接 参数好像很大,所以应该肯定是程序出 问题了。后来在测试那也出现了同样的问题,因为测试的人比较多,所以那两天基本上一两个小时连接池就满了,当时只能一次一次的重启tomcat。
因为XX系统之前已经修改过一次因为 代码的 错误而导致的 数据库连接没有释放的问题,所以这一次的问题比较不好定位,不能知道是哪些操作的连接池没有释放。
后来由zhxy提供了如下的查看当前数据库( 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
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
DBCC execution completed. If DBCC printed error messages, contact a user with System Administrator (SA) role.
SQL Text : set CHAINED off
后来发现每登录一次审判系统,使用上面的脚本执行结果就会有一个连接没有被释放(一般连接会在一段时间之后释放),除非是重启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();
}
}
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()问题就没有了,后来经zhangjy提醒,如果是使用spring提供的getSession()获取的连接,最好是使用releaseSession()方法进行释放。引用原话“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 ));
// ……
});
" {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 ));
// ……
});