處理超出打開遊标的最大數異常(ORA-01000: maximum open cursors exceeded)
ORA-01000_maximum_open_cursors_exceeded_
在執行如下代碼時,經常會出現ora-01000: maximum open cursors exceeded異常
1 for(int i=0;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
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關閉。