我正在更改旧的 Spring 应用程序以使用动态配置的连接池。整个系统中的所有查询都使用 JdbcTemplate,或者通过 Jms 连接到 Oracle AQ——我已经检查过,每次获得 ResultSet 时,它都会在 finally{} 块内的框架代码中关闭。在我转向动态配置的连接池之前,该应用程序从未遇到任何问题。我目前使用 C3P0 作为我的连接池,但是使用 DBCP2 和 Atomikos 池也会出现同样的问题。这是问题:
在处理任意时间后(它似乎因应用程序正在执行的操作而有所不同,但最终总是会发生)我用完了连接。出于绝望,我已将连接数提高到 10k,但这只会延长不可避免的时间。我运行这个查询:
select a.value, s.username, s.sid, s.serial# from v$sesstat a, v$statname b, v$session s
where a.statistic# = b.statistic# and s.sid=a.sid and b.name = 'opened cursors current'
and s.username = '<my app>' order by a.value asc ;
果然,
VALUE USERNAME SID SERIAL#
------ ------------------------------ ---------- ----------
1 MY_APP 69 16665
2 MY_APP 149 9703
3 MY_APP 13 38401
4 MY_APP 100 8629
4 MY_APP 145 26291
29 MY_APP 49 30425
2997 MY_APP 147 33539
5317 MY_APP 52 12599
6425 MY_APP 102 14803
10000 MY_APP 19 18469
好的,我已经知道我的游标用完了。我知道我在网上看到的下一条建议是运行以下查询:
select sql_text, sid, count(*) from v$open_cursor where user_name = '<my app>'
group by sql_text, sid having count(*) > 1 ;
我一共得到了九排……这些家伙超过了三排。如果我删除“have”子句并将所有游标计数加起来,则大约有 120 个游标。不是10k!
select sql_text, count(*) from v$open_cursor where sid = 19 group by sql_text having count(*) > 1 ;
SQL_TEXT COUNT(*)
-------------------------------------------------------- ----------
update seq$ set increment$=:2,minvalue=:3,maxvalue=:4,cycle# 41
这是应用程序从 JdbcTemplate 正确(也隐式地)关闭的插入语句中隐式获取新序列值。即便如此,41和10k不在一个范围内!
我知道 v$open_cursor 表仅代表“缓存”游标,并且“当前打开的游标”统计中计数的一些游标可能已经标记为“可关闭”。有没有办法弄清楚这些打开的游标中涉及的确切进程或sql?
我尝试了几种方法来试图弄清楚应用程序发生了什么。我创建了一个特殊的数据源包装器,它将包装所有连接、语句、准备好的语句、可调用语句及其所有生成的结果集,将它们保存在内存中的特殊集合中,并在关闭时将它们删除。我以为我可以通过这种方式捕捉到它——相反,通过这个池打开的每一个资源都被关闭了。每最后一个。我什至在连接池和弹簧引擎的内部进行了调试,以观察 Oracle 瘦 ForwardOnlyResultSet 被标记为已关闭。
我几乎可以怀疑问题出在与 AQ 的交互上,只是这种机制在旧版本的应用程序中运行良好。
我什至尝试通过显式捕获 ORA-01000 错误并将其用作硬重置连接池的触发器来解决该问题。只要它在处理简单的任务,它就可以工作,但是对于某些进行数千次数据库操作的大型进程,我在进程完成之前就失去了连接;硬重置连接池会使事务无效,整个事情进入无限循环。情况不好,四面八方。
我完全不知所措......从应用程序方面来看,它看起来完全无懈可击,而从数据库方面来看,通过查看 v$open_cursor,它看起来不像我有很多打开的游标,只是在统计。不幸的是,统计数据似乎很重要。
帮助?