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
顯然是連接池滿了,駐地工程師重啟之后就可以正常使用了。因為我們的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
使用該腳本只能之后,執行結果都是打印出大量類似的下面的三行:
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()問題就沒有了,后來經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));
// ……
});
連接池的問題解決。
posted on 2008-08-30 15:03
歲月如歌 閱讀(7186)
評論(2) 編輯 收藏 所屬分類:
java