调试 ORA-01000

Debugging ORA-01000

我正在修改旧版 Spring 应用程序以使用动态配置的连接池。整个系统中的所有查询都使用 JdbcTemplate,或者通过 Jms 连接到 Oracle AQ——我已经检查过,每次获得 ResultSet 时,它都会在 finally{} 块内的框架代码中关闭。在我转向动态配置的连接池之前,该应用程序从未遇到过任何问题。我目前正在使用 C3P0 作为我的连接池,但是使用 DBCP2 和 Atomikos 池也会出现同样的问题。这是问题所在:

在处理任何时间长度后(它似乎根据应用程序正在做的事情而有所不同,但最终总是会发生)我 运行 连接中断。出于绝望,我将连接数增加到 10k,但这只会延长 inevitable。我 运行 这个查询:

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

好吧,我已经知道我运行没有游标了。我知道我在网上看到的下一条建议是 运行away 资源泄漏是什么 运行 类似于以下查询:

select sql_text, sid, count(*) from v$open_cursor where user_name = '<my app>'
group by sql_text, sid having count(*) > 1 ;

我一共找回了九行……这些家伙不超过三行。如果我删除 "having" 子句并加总所有游标计数,它大约有 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 table 仅代表 "cached" 个游标,并且 "opened cursors current" 统计中计算的一些游标可能已经标记为"closeable"。有什么方法可以弄清楚这些打开的游标中涉及的确切过程或 sql?

我尝试了几种方法来弄清楚应用程序正在发生什么。我创建了一个特殊的数据源包装器,它将包装所有连接、语句、准备好的语句、可调用语句及其所有生成的结果集,将它们保存在内存中的特殊集合中,并在关闭时删除它们。我以为我可以用这种方式捕获它——相反,通过这个池打开的每一个资源都被关闭了。每一个最后一个。我什至在连接池和 spring 引擎的内部进行了调试,以观察 Oracle thin ForwardOnlyResultSet 被标记为已关闭。

我几乎可以怀疑问题出在与AQ的交互上,除了这个机制在旧版本的应用程序中运行得很好。

我什至尝试通过显式捕获 ORA-01000 错误并将其用作硬重置连接池的触发器来解决该问题。只要它在处理简单的任务,它就可以工作,但是对于某些正在进行数千个数据库操作的大型进程,我在进程完成之前就失去了连接;硬重置连接池会使事务无效,整个过程将进入无限循环。糟糕的情况,到处都是。

我完全不知所措......从应用程序方面来看,它看起来完全无懈可击,而从数据库方面来看,通过查看 v$ 看起来我没有很多打开的游标open_cursor,仅在统计中。不幸的是,统计数据似乎很重要。

帮忙?

您的第一个 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 应谨慎使用并意识到您可能无法获得预期的优化器计划。