DBCP连接池优化之启用PoolPreparedStatements

一、背景

公司的应用系统后台运行日志显示,系统经常出现“ORA-01000: maximum open cursors exceeded ”错误,即“超出最大可打开的游标数”。
应用系统采用SSH构建,使用commons-dbcp作为连接池,数据库为Oracle 10g。

二、分析处理

  • 查看游标使用情况
SELECT o.sid, osuser, machine, COUNT(*) num_curs
  FROM v$open_cursor o, v$session s
 WHERE user_name = &user_name
   AND o.sid = s.sid
 GROUP BY o.sid, osuser, machine
 ORDER BY num_curs DESC;
  • 查看游标执行情况
SELECT o.sid, q.sql_text
  FROM v$open_cursor o, v$sql q
 WHERE q.hash_value = o.hash_value
   AND o.sid = &sid;
  • 怀疑使用hql语句或sql语句查询时,直接将参数作为语句字符串的一部分,而不是使用绑定变量,导致Oracle执行相同逻辑的SQL时每次创建新的执行计划,使游标不断增加。如:
// 直接将参数作为hql语句字符串的一部分
String hql="from User u where u.username='"+userName+"'";
Query query = getSession.createQuery(hql);
query.list();
// 使用绑定变量
String hql="from User u where u.username=?";
Query query = getSession.createQuery(hql);
query.setString(1, userName);
query.list();

根据游标执行情况排查了产生游标数较多的hql或sql,排除了“未使用绑定变量,导致游标不断增加”的可能性。

  • 判断可能是DBCP连接池使用不当导致
    经互联网搜索,发现配置DBCP连接池启用参数poolPreparedStatements即可解决此问题。
    查看org.apache.commons.dbcp.BasicDataSource源码,可以看到:
/**
 * Prepared statement pooling for this pool. When this property is set to true
 * both PreparedStatements and CallableStatements are pooled.
 */
protected boolean poolPreparedStatements = false;

/**
 * Returns true if we are pooling statements.
 * 
 * @return true if prepared and callable statements are pooled
 */
public synchronized boolean isPoolPreparedStatements() {
    return this.poolPreparedStatements;
}

即启用poolPreparedStatements后,PreparedStatements 和CallableStatements 都会被缓存起来复用,即相同逻辑的SQL可以复用一个游标,这样可以减少创建游标的数量。

三、参考文章

连接池优化之启用PoolPreparedStatements
关于ORA-01000: maximum open cursors exceeded” 问题分析总结

你可能感兴趣的:(Java,J2EE)