
明明是他的应用系统中,每次使用完了CURSOR都会做一个关闭操作,为什么还会有那么多的CURSOR都是OPEN的呢。DBA拿了这个查询的结果来找他,让他们整改应用,说是他们的应用存在CURSOR泄露。
从上面这个结果上看,大量的会话中打开的CURSOR的数量超过200个,甚至高达2622个。一般的应用程序都会在使用完CURSOR后关闭CURSOR,怎么会产生这么多的OPEN状态的CURSOR呢?难道我们碰到了CURSOR泄漏呢?如果出现了CURSOR泄漏,那么为什么应用不会报错,而系统还能够正常运行呢?要回答这个问题,就需要了解CURSOR关闭的相关常识了。
每个会话中都会存在一个CURSOR缓冲池,当一个CURSOR使用完毕后,程序就会将这个CURSOR关闭,正常情况下,处于UGA和共享池中的CURSOR就应该也关闭了。不过为了确保CURSOR能够被有效的共享,Oracle使用了一种缓冲机制,来更好的让CURSOR能被更广泛的共享。Oracle把CURSOR关闭设计为为软关闭和硬关闭两种。软关闭的CURSOR虽然客户端程序关闭了CURSOR,但是实际上在CURSOR缓冲中,CURSOR还是处于OPEN状态,下回这个会话要执行这条SQL的时候,不需要再重新打开CURSOR了,而这个CURSOR在共享池中的共享部分,也因为还有会话存在引用而不会轻易被AGEOUT。这也是我们往往能够发现自己的会话中存在大量的OPEN状态的CURSOR的主要原因。实际上,如果系统没有出现CURSOR溢出,存在大量的软关闭的CURSOR不会对系统产生太大的影响,Oracle 会自动维护这个CURSOR缓冲区,使之较为高效的运作,同时不会因为OPEN CURSOR数量太多而导致正常使用的CURSOR无法打开。
当然Oracle也不会让一个会话的OPEN CURSOR无限制的增长,否则某个会话真的出现了CURSOR泄露,就会把整个数据库系统搞崩溃。Oracle通过Open_cursors参数来控制一个会话可以OPEN的CURSOR的总量。这个参数的缺省值是300,一般来说,对于目前具有较大SGA的数据库系统来说,这个参数偏低了,设置为3000可以让CURSOR的共享更为充分。当一个会话打开的CURSOR数量过多的时候,就会自动将一些本会话中客户端已经关闭的CURSOR真正的关闭掉。
只有当CURSOR硬关闭了,这个CURSOR和会话之间的联系才彻底断掉了。下回这个会话要执行这个SQL,就需要重新创建CURSOR相关数据结构了。
如果真的出现了CURSOR泄露,我们的应用程序在某个CURSOR使用完了以后没有关闭,那么这个会话的opened cursors current统计值会越来越大,当这个值达到OPEN_CURSORS 参数的限制之后,如果还在OPEN 新的CURSOR,而老的CURSOR均处于OPEN状态,那么应用前端就会报错ORA-01000。

这种情况在JAVA的应用程序中更容易出现。因为其他的程序,比如C/PYTHON,哪怕我们的程序中没有显示的关闭STATEMENT等和CURSOR相关的对象,只要程序退出,就会自动释放所有的资源了。而JAVA应用就不同了,当某个JAVA OBJECT执行完毕后,不会自动释放所有的资源。而是由JVM的GC来负责清理。而GC清理这些对象的时候,只会回收内存而不会考虑ORACLE 的内部资源。当JDBC连接池中的会话还一直在保持的时候,UGA中的CURSOR的状态并不会因为JVM的GC而发生任何的改变。这个情况一旦发生,那么这个会话再打开一个新的CURSOR的时候,就有可能出现ORA-01000。
当我们发现系统中出现了ORA-01000的时候,我们可以通过v$open_cursor这个视图去查看哪些CURSOR是一直被打开的,通过查找SQL_TEXT我们就可以找到一些蛛丝马迹,再和开发商一起分析,到底哪个地方的代码出了问题。




