【问题标题】:Debugging ORA-01000调试 ORA-01000
【发布时间】:2016-09-19 10:57:15
【问题描述】:

我正在更改旧的 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 ;

我总共返回了 9 排……这些家伙不能超过 3 排。如果我删除“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,我看起来并没有很多打开的游标,只在统计中。不幸的是,统计数据似乎很重要。

帮助?

【问题讨论】:

    标签: oracle connection-pooling spring-jdbc oracle-aq ora-01000


    【解决方案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,因为有 bug)

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

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

    【讨论】:

    • 您好,感谢您的回答。虽然可能有少数查询不使用绑定变量,但这些查询根本没有任何变量。其余的都使用绑定。
    • 嗨,比尔,“没有任何变量”是问题所在。在 SQL 语句中有文字的任何地方,都应该有一个绑定变量。例如 select * from sales where lastname='FRANZ' 应该是 select * from sales where lastname=:1 。如果您打开 cursor_sharing=FORCE,那么数据库将为您执行此操作。
    • 这是一篇关于如何识别应该使用绑定变量但不使用的语句的文章:connormcdonald.wordpress.com/2016/05/30/…
    猜你喜欢
    • 1970-01-01
    • 2011-08-14
    • 2015-03-29
    • 2012-08-24
    相关资源
    最近更新 更多