第一步:核查Oracle數據庫 的游標的最大數
處理超出打開游標的最大數異常(ORA-01000: maximum open cursors exceeded)
ORA-01000_maximum_open_cursors_exceeded_
在執行如下代碼時,經常會出現ora-01000: maximum open cursors exceeded異常
1 for(int i=0;i<balancelist.size();i++) 2 { 3 prepstmt = conn.prepareStatement(sql[i]); 4 prepstmt.setBigDecimal(1,nb.getRealCost()); 5 prepstmt.setString(2, adclient_id); 6 prepstmt.setString(3, daystr); 7 prepstmt.setInt(4, ComStatic.portalId); 8 prepstmt.executeUpdate(); 9 }
1. 檢查數據庫中的 OPEN_CURSORS 參數值。
1 --Oracle 使用 init.ora 中的初始化參數 OPEN_CURSORS 指定一個會話一次最多可以擁有的游標數。缺省值為 50。要獲得數據庫中 OPEN_CURSORS 參數的值,可以使用以下查詢: 2 SQL> show parameter open_cursors; 3 NAME TYPE VALUE 4 ------------------------------------ ----------- --------------- 5 open_cursors integer 300 6 7 修改open_cursors 8 9 SQL> alter system set open_cursors=1000; 10 11 系統已更改。 12 13 SQL> commit; 14 15 提交完成。 16 17 SQL> show parameter open_cursors; 18 19 NAME TYPE VALUE 20 ------------------------------------ ----------- --------------------- 21 open_cursors integer 1000 22重要的是將 OPEN_CURSORS 的值設置得足夠大,以避免應用程序用盡所有打開的游標。應用程序不同,該值也不同。即便會話打開的游標數未達 OPEN_CURSORS 指定的數量(即設置的值高於實際需要的值),也不會增加系統開銷。
2. 獲取打開的游標數。
下面的查詢按降序顯示用戶“SCOTT”為每個會話打開的游標數。1 SQL> select o.sid, osuser, machine, count(*) num_curs 2 2 from v$open_cursor o, v$session s 3 3 where user_name = 'SCOTT' and o.sid=s.sid 4 4 group by o.sid, osuser, machine 5 5 order by num_curs desc; 6 SID OSUSER MACHINE NUM_CURS 7 ----------------------------------------------------- 8 217 m1 1000 9 96 m2 10 10 411 m3 10 11 50 test 9
請注意,v$open_cursor 可以跟蹤會話中 PARSED 和 NOT CLOSED 的動態游標(使用 dbms_sql.open_cursor() 打開的游標)。它不會跟蹤未經分析(但已打開)的動態游標。在應用程序中使用動態游標並不常見。本模式的前提是未使用動態游標。
3. 獲取為游標執行的 SQL。
使用在以上查詢結果中找到的 SID 運行下面的查詢:
1 SQL> select q.sql_text 2 2 from v$open_cursor o, v$sql q 3 3 where q.hash_value=o.hash_value and o.sid = 217; 4 SQL_TEXT 5 select * from empdemo where empid='212' 6 select * from empdemo where empid='321' 7 select * from empdemo where empid='947' 8 select * from empdemo where empid='527' 9 ...
結果將顯示正在連接上執行的查詢。它提供了一個入手點,讓您可以反向跟蹤到打開游標的來源。
這樣的錯誤很容易出現在Java代碼中的主要原因是:Java代碼在執行conn.createStatement()和 conn.prepareStatement()的時候,實際上都是相當與在數據庫中打開了一個cursor。尤其是,如果你的 createStatement和prepareStatement是在一個循環里面的話,就會非常容易出現這個問題。因為游標一直在不停的打開,而且沒有關閉。
一般來說,我們在寫Java代碼的時候,createStatement和prepareStatement都應該要放在循環外面,而且使用了這些 Statment后,及時關閉。最好是在執行了一次executeQuery、executeUpdate等之后,如果不需要使用結果集(ResultSet)的數據,就馬上將Statement或PreparedStatement關閉。
對於出現ORA-01000錯誤這種情況,單純的加大open_cursors並不是好辦法,那只是治標不治本 。 實際上,代碼中的隱患並沒有解除。
而且,絕大部分情況下,open_cursors只需要設置一個比較小的值,就足夠使用了,除非有非常特別的要求。
如果你不使用連接池,那么就沒有什么問題,一旦Connection關閉,數據庫物理連接就被釋放,所有相關Java資源也可以被GC回收了。
但是如果你使用連接池,那么請注意,Connection關閉並不是物理關閉,只是歸還連接池,所以PreparedStatement和 ResultSet都被持有,並且實際占用相關的數據庫的游標資源,在這種情況下,只要長期運行,往往就會報“游標超出數據庫允許的最大值”的錯誤,導致程序無法正常訪問數據庫。
正確的代碼,如下所示:
1 for(int i=0;i<balancelist.size();i++) 2 { 3 prepstmt = conn.prepareStatement(sql[i]); 4 prepstmt.setBigDecimal(1,nb.getRealCost()); 5 prepstmt.setString(2, adclient_id); 6 prepstmt.setString(3, daystr); 7 prepstmt.setInt(4, ComStatic.portalId); 8 prepstmt.executeUpdate(); 9 prepstmt.close(); 10 }
在執行了一次executeQuery、executeUpdate等之后,如果不需要使用結果集(ResultSet)的數據,就馬上將Statement或PreparedStatement關閉。
——————————————————————————————————————————————————————————————————————————————————————————