1

我正在更改旧的 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,它看起来不像我有很多打开的游标,只是在统计。不幸的是,统计数据似乎很重要。

帮助?

4

1 回答 1

1

您的第一个 SQL 显示会话 19 有 10,000 个打开的游标,而您的第三个 SQL 显示只有一个是共享的。结论,会话 19 有近 10,000 个唯一的打开游标。造成这种情况的通常原因是使用了不可共享的 SQL。即,您没有在代码中使用任何绑定变量。这意味着每次您的应用程序运行 SQL 语句时,数据库都会为该语句打开一个新游标。当你的应用程序运行了 10000 条语句时,你就碰壁了。

要解决此问题,您可以设置数据库参数 CURSOR_SHARING=FORCE 。这将导致数据库自动将绑定变量替换为 SQL 中的文字,从而使您的 SQL 语句可共享。然后,您的应用程序每次运行 SQL 语句时都不会打开单独的游标。

这是一篇关于 CURSOR_SHARING=FORCE 的短文:http ://www.dba-oracle.com/t_cursor_sharing_force.htm

如果您使用的是 Oracle 9i 或更高版本,则可以改用 CURSOR_SHARING=SIMILAR。(虽然一些 DBA 建议在 Oracle 11 或更高版本之前不要使用 SIMILAR,因为存在错误)

设置 CURSOR_SHARING 应该被认为是快速修复,而不是最终解决方案。从长远来看,您应该重写您的应用程序,以便它使用绑定变量而不是使用 CURSOR_SHARING 来补偿。这是一个很好的解释: https ://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:5180609822543

最后 - 设置 CURSOR_SHARING 应该谨慎使用,并意识到您可能无法获得预期的优化器计划。

于 2016-05-22T16:01:30.937 回答