一、背景
公司的应用系统后台运行日志显示,系统经常出现“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 <code>true</code>
* 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” 问题分析总结