Java_oracle超出打開游標的最大數的原因和解決方案



第一步:核查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關閉。














——————————————————————————————————————————————————————————————————————————————————————————


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM