1. 程式人生 > >oracel出現超開啟遊標的最大值原因及解決辦法

oracel出現超開啟遊標的最大值原因及解決辦法

錯誤型別:ORA-01000: maximum open cursors exceeded  超出開啟遊標的最大數、

原因分析:Java程式碼的時候,createStatement和prepareStatement都應該要放在迴圈外面,而且使用了這些Statment後,及時關閉。最好是在執行了一次executeQuery、executeUpdate等之後,如果不需要使用結果集(ResultSet)的資料,就馬上將Statement或PreparedStatement關閉。


解決方案: 在執行如下程式碼時,經常會出現ora-01000: maximum open cursors exceeded異常
for(int i=0;i<balancelist.size();i++)
{
prepstmt = conn.prepareStatement(sql[i]);
prepstmt.setBigDecimal(1,nb.getRealCost());
prepstmt.setString(2, adclient_id);
prepstmt.setString(3, daystr);
prepstmt.setInt(4, ComStatic.portalId);
prepstmt.executeUpdate();
}

  1. 檢查資料庫中的 OPEN_CURSORS 引數值。

  Oracle 使用 init.ora 中的初始化引數 OPEN_CURSORS 指定一個會話一次最多可以擁有的遊標數。預設值為 50。要獲得資料庫中 OPEN_CURSORS 引數的值,可以使用以下查詢:
SQL> show parameter open_cursors;
NAME TYPE VALUE
------------------------------------ ----------- ---------------
open_cursors integer 1000

  重要的是將 OPEN_CURSORS 的值設定得足夠大,以避免應用程式用盡所有開啟的遊標。應用程式不同,該值也不同。即便會話開啟的遊標數未達 OPEN_CURSORS 指定的數量(即設定的值高於實際需要的值),也不會增加系統開銷。

  2. 獲取開啟的遊標數。

下面的查詢按降序顯示使用者“SCOTT”為每個會話開啟的遊標數。
SQL> select o.sid, osuser, machine, count(*) num_curs
2 from v$open_cursor o, v$session s
3 where user_name = 'SCOTT' and o.sid=s.sid
4 group by o.sid, osuser, machine
5 order by num_curs desc;
SID OSUSER MACHINE NUM_CURS
-----------------------------------------------------
217 m1 1000
96 m2 10
411 m3 10
50 test 9
請注意,v$open_cursor 可以跟蹤會話中 PARSED 和 NOT CLOSED 的動態遊標(使用 dbms_sql.open_cursor() 開啟的遊標)。它不會跟蹤未經分析(但已開啟)的動態遊標。在應用程式中使用動態遊標並不常見。本模式的前提是未使用動態遊標。

  3. 獲取為遊標執行的 SQL。

使用在以上查詢結果中找到的 SID 執行下面的查詢:
SQL> select q.sql_text
2 from v$open_cursor o, v$sql q
3 where q.hash_value=o.hash_value and o.sid = 217;
SQL_TEXT
select * from empdemo where empid='212'
select * from empdemo where empid='321'
select * from empdemo where empid='947'
select * from empdemo where empid='527'
...
結果將顯示正在連線上執行的查詢。它提供了一個入手點,讓您可以反向跟蹤到開啟遊標的來源。

  這樣的錯誤很容易出現在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都被持有,並且實際佔用相關的資料庫的遊標資源,在這種情況下,只要長期執行,往往就會報“遊標超出資料庫允許的最大值”的錯誤,導致程式無法正常訪問資料庫。
正確的程式碼,如下所示:
for(int i=0;i<balancelist.size();i++)
{
prepstmt = conn.prepareStatement(sql[i]);
prepstmt.setBigDecimal(1,nb.getRealCost());
prepstmt.setString(2, adclient_id);
prepstmt.setString(3, daystr);
prepstmt.setInt(4, ComStatic.portalId);
prepstmt.executeUpdate();
prepstmt.close();
}

  在執行了一次executeQuery、executeUpdate等之後,如果不需要使用結果集(ResultSet)的資料,就馬上將Statement或PreparedStatement關閉

http://my.oschina.net/zenglingfan/blog/62746詳情看這篇文章的介紹