请教jdon关于连接池对应的游标问题

我用的是oracle8i的数据库,9ias903服务器。
用连接池的时候发现游标数长的很快,而数据库连接池的连接数很稳定。(通过
select count(*) from v$session 和select count(*) from v$open_corsor查看获知)
一天之内连接数已经涨到900个左右,而连接数稳定在10个左右。可以排除没有关闭statement造成的游标没关闭。我做过实验,statement关闭后一般open cursor就关闭了,但是如果是调用存储过程的话(而存储过程里面又打开cursor进行操作,然后关闭cursor),这样的话,即使关闭statement,但通过select * from v$open_cursor还是可以看到它的。估计是数据库吧它cache了???因为我再次重复调用该页面,open cursor数就不加了,但是如果并发多个用户调用的话(或则狂按f5刷新页面),open cursor数就“蹭蹭”上去了。而kill掉某个连接的话,它对应的open cursors就没掉了。但是这样的话这是游标数好象是一直往上涨的。不知道会不会对系统有影响。有无解决方法?

jvm是hotspot 1.3 。我想如果用1.4的会好一些,因为1.4jvm的垃圾回收是可并行的。但是好象不知道哪里知道的oracle9ias903在1。4下会有问题。。

现在我们的系统还是有问题的.就是有时候会不挺的死掉,重起1,2个小时后就死了.有时候运行几天也很正常。有时候变的很慢,查看进程,发现有一个oc4j进程一直在占用cpu资源,从60%到30%不等。而且一直在运行几个小时。而这时系统的访问量已经很少了。oracle的人来了说是我们db connection没有关闭的问题,但是我们现在重整了后,statement和connnection应该是完全关闭了。但还是有那个问题。我以为是因为在做垃圾回收的 时候出现问题,所以调大了jvm heap的新生代的大小,现在是320m,新生代(推荐新生代大小为jvm heap总大小的1/4,但是为了保险,我弄的更大了)。现在我们的参数配置是:-server^@-verbose:gc^@-XX:NewSize=320m^@-XX:MaxNewSize=320m^@-XX:SurvivorRatio=8^@-Xms768m^@-Xmx768m^。
原先设置了1G heap 大小。但是发现很快进程就上到了1G(几个小时)。现在增大新生代后,虽然增长比较慢,但还是有增加的趋势。一天大概10-20m的增加吧。查看垃圾回收记录,它占用的内存还是不大的,现在是318349K->56717K(753664K)。实际占用的也就50多m.我们的平台是:
oracle9ias903,oracle8i(9i),redhat advaced server2.1.硬件配置.双700m cpu,2G的pc server.系统性能不是瓶径。用户总量不大(100来个左右),并发高峰期也就30-40来个。架构是jsp+javabean实现。哪位大侠帮忙分析一下可能原因及解决方法。多谢了!(我已经搞了几个星期了,虽然解决了不少问题,但最终的问题还没有解决:( )


我认为还是你Jsp+JavaBeans架构选择欠妥,这种结果小系统可以,对于你这样系统问题就多了。

使用Jsp+JavaBeans无法从代码上真正限制connection的关闭,因为Jsp运行是按照线程运行的,并发时先后次序不定,因此,你按照你想像的逻辑检查发现没有connection没有关闭,实际情况还是不一定。


还有,存储过程语言一定要少用,我建议开发J2EE系统采用面向Model模型设计,这样,数据库只是持久层的一个具体实现,在J2EE中是一个非常具体次要的位置,不涉及数据库。这样,将原来集中在数据库上的负载可以分散到中间件EJB上,而EJB可以分布式集群计算,无疑提高了原来单纯数据库系统的运算性能。

真不好意思,你这个具体问题因为不在现场无法详细回答,以上仅仅供参考。

谢谢答复。我们的系统是2000期间开发的 ,现在做重整。说是javabean+jsp,其实唯有的几个类除了一两个web helper类外,主要就是一个封装数据库操作的类(其他都是jsp),该类主要有几个类似如下的方法:
ResultSet ExecuteQuery(String sql)。所有的jsp页面都是调用该方法获取resultset,然后在jsp里关闭相应的数据库资源。但是该类封装及为混乱(有多个public的statement,connection成员变量)。导致我们想通过修改该类来解决jsp页面里面的statement ,connection资源泄露的想法不可行。最后只好自己写了个cache(MyResultSetCache implements ResultSet),让上面说的ExecuteQuery(String sql)方法返回该cache类。然后直接在该方法的finally关闭resultset,statement,connection等。虽然这样性能会差一点,但是我们的性能不是瓶经。所以说我们基本是解决了statement,connection没有关闭的问题。但是系统仍旧存在上述问题。这两天修改了jvm参数,希望能够解决该问题。。(只是在观察中,不敢向老板说解决了该问题)。

good

对于第一种情况,我们推测可能是网络不稳定的原因导致的连接池中连接坏死,这样一直积累下去导致连接池用光。原来一直select count(*) from v$session 查看当时的连接数,看起来很正常。但这代表的只是数据库端检测到的连接数。刚刚想起来也许可能通过netstat来查看。由于对unix不是很熟,不知道这种情况下坏死的连接查看到的状态应该是什么?我刚刚用netstat |grep 1521看了一下,大部分的连接都是ESTABLISHED,但有3个是 TIME_WAIT,不知道这TIME_WAIT是否可以代表坏死的连接,或则什么状态才代表坏死的连接?

数据库端可以查看打开多少连接池,比如PhpMyADmin可以方便查看MySQL开启了多少连接。

数据库端查看的连接只是从数据库的角度来看的。我们做过实验,把数据库的网线断掉一会儿,重新连上。然后从数据库端查看已经打开的连接是0,而实际上应用服务器端的连接已经全部不能用了(我们设置了最大5个,最大空闲数5个的连接配置),说明这个时候池中的连接已经全部坏死了。

根据我对Oracle Jdbc的研究,oracle的每一个Statement同时最多只有一个ResultSet(创建新的ResultSet的时候原有的一个会被释放),关闭Statement服务器端的相应Cursor会被释放,而关闭Connection的时候全部相关的Statement都会被释放,因此你保证关闭所有的Statement然后重用Connection或者合理关闭Connection全部资源都会被释放,不会有任何问题。但是如果你是从服务器端kill session,相应资源是否会被立刻释放我不是很清楚,但是可以肯定的是如果jdbc端的Connection被清除的话,这些资源最终肯定会被释放(只是需要一定的时间,根据你后台参数的设置)。Oracle后台Cursor是很占资源的,一定要关闭,而且有参数可以配置每个连接最多只能开有多少个Cursor(好像默认配置是50)。因此,最好的办法还是从jdbc端直接把需要清除的Connection显示的关闭来释放资源更安全可靠!
至于你提到的存储过程中使用的Cursor没有被立即关闭,我想,正如你所说的,这应该是Oracle的优化策略吧,与jdbc无关。

TIME_WAIT, Closed_wait都是一些中间状态,属于正常(除非这些状态很多)。具体意义我不太清楚,但我这里经常有这些状态,并不影响使用或者性能。

我有一种资源跟踪的方法,不只能否用再你那里,大致思路

采用ThreadLocal和Servlet Filter来监测那些jsp(或者servlet)没有合理关闭Connection.

首先创建一个ThreadLocal的List变量,在获取Connection的时候,Connection存入该list。
Filter在处理完其他处理后,监测list中的Connection的状态是否已经全部关闭,没有关闭log下来,提醒修改程序,然后强行是否全部资源,顷空list。(如果你是采用type 1的connection,则在return connection to pool的时候从list remove connection)

刚刚试了一下,发现那些坏掉的连接在netstat下显示的居然还是established.不知道我有没有搞错,各位如果有时间,也试试看

同样,如果从数据库服务端kill掉一个session,只是对于数据库端来说,减少了一个connection.但对于connection pool,却反而只能导致对应的连接坏死。

我们也遇到过这个问题,有两个办法:
1是把所有或嫌疑比较大的preparestatement改成statement
2是按照下面的办法查找代码中游标泄漏的部分

大多数使用Oracle的程序员都会遇到另一个不充足的数据库资源:游标。Oracle为每一个SQL SELECT语句都创建一个数据库游标,并且维护那个光标直到应用程序关闭该语句。在JDBC应用程序中,关闭该语句就意味着调用用来运行该SQL语句的Statement对象 或 PreparedStatement对象的close()方法。从一个现存的Statement对象或PreparedStatement对象执行一个新的查询也会关闭与前一个查询相关的游标。

考虑下面的代码:

PreparedStatement pstmt = dbCon.prepareStatement("select * from emp where emp_id=?");
pstmt.setInt(1, 422);
ResultSet rs = pstmt.executeQuery();
// processing of result set
pstmt = dbCon.prepareStatement("select * from customers where cust_id = ?");
// etc
上面的代码段有一个主要的问题。如果该代码是用普通的Statement对象写的,那么它很可能已经通过多次调用executeQuery(String)方法创建了ResultSet对象。但是,因为上面的代码使用的是PreparedStatement对象而不是普通的Statement对象,它需要显式地关闭第一个对象而不是简单地在同一个地方创建一个新的对象。上面的代码,在连接自身被关闭之前将会用"select * from emp where emp_id=?"游标扰乱数据库。

尽管这个bug似乎相当明显,Java和Oracle的程序员往往都假设这些资源会被自动释放。由于Oracle JDBC驱动器的体系结构,Java垃圾收集器(garbage collector)不会清理那些孤立的PreparedStatement对象,并且,在大容量的应用程序中游标溢出(cursor leak)会很快累积。因此,假设PreparedStatement对象会自动释放是程序员最常犯的错误之一。

如果你已经访问了SYS.V_$OPEN_CURSOR视图,下面的SQL语句能够容易地发现游标溢出:

select user_name, sql_text, count(*) from sys.v_$open_cursor
group by user_name, sql_text
如果你在一个常规的查询或一个触发器中单独使用该语句执行PL/SQL过程,那么关闭Statement对象尤为重要。如果PL/SQL过程创建了多个隐式游标,在父语句被关闭之前,即使它们的工作完成了,它们有时也不能被恰当地关闭。