oracle超過最大游標數異常分析(轉貼)


問題描述 
Oracle 使用 OPEN_CURSORS 參數指定一個會話一次最多可以打開的游標的數量。超過此數量時,Oracle 將報告 ORA-01000 錯誤。當此錯誤傳播到 WebLogic Server 時,就會拋出 SQLException

 

java.sql.SQLException: ORA-01000: maximum open cursors exceeded

 

本模式闡述在使用 WebLogic Server 時出現該錯誤的可能成因及解決辦法。

故障排除
請注意,並非下面所有任務都需要完成。有些問題僅通過執行幾項任務就可以解決。

快速鏈接

 

診斷查詢 
以下 SQL 查詢有助於診斷 ORA-01000 問題。要執行這些查詢,需要以管理員身份登錄數據庫,或獲得數據庫管理員從那些 v$ 視圖中進行選擇的授權。

1. 檢查數據庫中的 OPEN_CURSORS 參數值。
Oracle 使用 init.ora 中的初始化參數 OPEN_CURSORS 指定一個會話一次最多可以擁有的游標數。缺省值為 50。遺憾的是,此缺省值通常對 WebLogic Server 這樣的系統來說過小。要獲得數據庫中 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 

 

在 WebLogic Server 中使用連接池時,此查詢中的 user_name 應為用於創建連接池的 user_name(假定是從連接池得到連接)。該查詢結果還給出了計算機名稱。請在查詢結果中找出打開游標數量大的 SID 和運行 WebLogic Server 的計算機的名稱。

請注意,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' 
...

 

結果將顯示正在連接上執行的查詢。它提供了一個入手點,讓您可以反向跟蹤到打開游標的來源。

返回頁首

常見成因及解決辦法 
下面是查找問題成因及可能的解決辦法的步驟。

代碼慣例 
此問題的最常見成因是未正常關閉 JDBC 對象。使用診斷查詢中第三個查詢的結果在應用程序代碼中反向跟蹤,確保將所有 JDBC 對象都正常關閉。BEA 建議在 finally 塊中顯式關閉 Connection、Statement 和 ResultSet 等 JDBC 對象,以確保無論是在正常還是異常情況下都將所有 JDBC 對象關閉。下面是一個常規示例:

 

Connection conn = null; 
Statement stmt = null; 
ResultSet rs = null;

try { 
conn = getConnection(); //Method getConnection will return a JDBC Connection 
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from empdemo"); 
// do work 
} catch (Exception e) { 
// handle any exceptions
} finally { 
try { 
if(rs != null) 
rs.close(); 
} catch (SQLException rse) {}
try { 
if(stmt != null) 
stmt.close(); 
} catch (SQLException sse) {}
try { 
if(conn != null) 
conn.close(); 
} catch (SQLException cse) {}
}

 

請避免采用任何放棄 JDBC 對象的代碼慣例。下面的代碼慣例在每個循環迭代中都獲得一個新的 Connection、Statement 和 ResultSet,但它沒有關閉每個迭代的 JDBC 對象。因此,它會導致 JDBC 對象泄漏。

 

Connection conn = null; 
Statement stmt = null; 
ResultSet rs = null; 
String[] queries = new String[10]; 
//Define queries

try { 
for(int i = 0; i < 10; i++) { 
conn = getConnection(); 
stmt = conn.createStatement(); 
rs = stmt.executeQuery(queries[i]); 
// do work 
} 
} catch (Exception e) { 
// handle any exceptions
} finally { 
try { 
if(rs != null) 
rs.close(); 
} catch (SQLException rse) {}
try { 
if(stmt != null) 
stmt.close(); 
} catch (SQLException sse) {}
try { 
if(conn != null) 
conn.close(); 
} catch (SQLException cse) {}
}

 

盡管根據 JDBC 規范的規定,關閉 Connection 時正常情況下也會將 Statement 和 ResultSet 關閉,但好的做法是:如果在一個 Connection 對象上創建了多個 Statement,則在使用完 Statement 和 ResultSet 后立即顯式將它們關閉。如果未立即顯式關閉 Statement 和 ResultSet,游標可能會積聚並在關閉 Connection 前超過數據庫允許的最大數量。例如,在以下代碼片斷中,正常情況下通過 finally 塊關閉 Connection 時,也會將 ResultSet 和 Statement 關閉。不過,此代碼片斷在一個連接上創建了多個 Statement 和 ResultSet。因此在循環完成前,可能已發生“超出最多允許打開的游標數”問題。

 

Connection conn = null;

try{ 
conn = getConnection();

for(int i = 0; i < NUM_STMT; i++) { 
Statement stmt = null; 
ResultSet rs = null; 

stmt = conn.createStatement(); 
rs = stmt.executeQuery(/*some query*/); 
//do work 
} 
} catch(SQLException e) { 
// handle any exceptions
} finally { 
try{ 
if(conn != null) 
conn.close(); 
} catch(SQLException ignor) {}
}




返回頁首

語句緩存
為提高性能,WebLogic Server 提供了一種功能,讓您可以在使用連接池時將預處理語句和可調用語句載入緩存。當 WebLogic Server 將預處理語句或可調用語句載入緩存時,在許多情況下,DBMS 將為每個打開的語句都保留游標。因此,語句緩存可能是“超出最多允許打開的游標數”問題的成因。“語句緩存大小”屬性決定在每個連接池實例中為每個連接緩存的預處理和可調用語句的總數。如果緩存的語句過多,可能會導致超過數據庫服務器上打開游標數的上限。

請注意,各版本 WebLogic Server 的缺省語句緩存大小是有差異的。示例:

要確定“超出最多允許打開的游標數”問題是否與語句緩存有關,可以通過將語句緩存大小設置為 0 將此功能關閉或減少緩存大小,再確認是否仍會出現錯誤。如果在減少緩存大小后問題沒有發生,則說明連接池原有的語句緩存過大或 DBMS 中打開游標數的上限過低。可能需要考慮調整其中的一個值。如果發現連接上打開的游標數持續增加,但在將語句緩存大小設置為 0 后沒有出現這種現象,則可能說明存在游標泄漏問題。這可能是由使用的 JDBC 驅動程序所致,也可能是 WebLogic Server 本身的一個錯誤。請嘗試使用其它 JDBC 驅動程序。如果使用其它 JDBC 驅動程序后仍發生同樣的問題,請將此問題報告給 BEA,這樣支持工程師可以對問題做進一步探查,以確定該問題是否為 WebLogic Server 自身的一個錯誤。

返回頁首

數據庫驅動程序 
“超出最多允許打開的游標數”問題的另一個可能成因是 JDBC 驅動程序有問題。為分清問題是驅動程序問題還是 WebLogic 連接池問題,如果有可重現的測試案例,可以嘗試執行以下步驟。

1. 直接從驅動程序獲取連接。
在測試案例中,繞過 WebLogic 連接池直接從驅動程序獲取 JDBC 連接。但請不要關閉連接,只需讓它們以數組或某種其它結構形式保持打開狀態,然后確認游標泄漏是否仍然存在。不關閉連接是因為要模擬使用連接池時的行為。使用連接池時,connection.close() 並未真正地關閉物理連接,而是將連接返回到池中。

2. 嘗試使用其它 JDBC 驅動程序。
可以嘗試使用其它供應商的 JDBC 驅動程序或升級版的驅動程序,然后確認問題是否仍然存在。可以使用元數據來驗證所使用的驅動程序是否正確。示例代碼與下面的類似:


Connection conn = getConnection(); 
DatabaseMetaData dmd = conn.getMetaData();
System.out.println("JDBC Driver Name is " + dmd.getDriverName()); 
System.out.println("JDBC Driver Version is " + dmd.getDriverVersion()); 


3. XA 驅動程序錯誤
如果使用的是 Oracle XA 驅動程序,並且數據庫中出現了大量類似“SELECT count (*) FROM SYS.DBA_PENDING_TRANSACTIONS”的查詢,則可能是 Oracle XA 驅動程序存在游標泄漏問題。在有關 MetaLink 的案例 3151681 中有對此問題的描述,並且版本 10.1.0.2 中已修正了該問題。
此外,在使用 XA 驅動程序時,請確保按在 http://e-docs.bea.com/wls/docs81/jta/thirdpartytx.html#1075181 (English) 中所述,在 Database Server 上啟用 XA(例如,grant select on dba_pending_transactions to public)。

如果問題是 JDBC 驅動程序問題,但又不得不使用該驅動程序,一種以變通方式解決游標泄漏問題的方法是不時重設 WebLogic 連接池,或收縮連接池。有關重設或收縮連接池的方法,請參閱 WebLogic 文檔(如果是 8.1 版本,該文檔位於 http://e-docs.bea.com/wls/docs81/ConsoleHelp/domain_jdbcconnectionpool_control.html (Enlish))。 

返回頁首

已知問題
您可以定期查看所用 WLS 版本的“Release Notes”,了解 Service Pack 中的“Known Issues”或“Resolved Issues”的詳細信息及瀏覽與 ORA-01000 /游標泄漏有關的問題。方便起見,下面提供了這些發行說明的鏈接: 對於需要特別注意之處,請參閱以下 CR,在相應版本 Service Pack 的發行說明中注明了已有針對它們的解決方法:
  • CR129379 - 如果 EJB 事務創建了許多新實體或以其它方式占用了許多 Bean,而這些 Bean 均使用 JDBC,WebLogic Server 就存在 Oracle 游標數量不足的風險,因為 WebLogic Server 在嘗試避免發生可疑的 Oracle 驅動程序錯誤時會將對 JDBC 語句的關閉推遲到事務結束后進行,同時也將語句的游標一直保留到那個時候。WLS 6.1 SP7WLS 7.0 SP5 和 WLS 8.1 SP3 中對此行為做了改動,會話已不必將游標一直保留到事務結束。
  • CR179600 - WLS 8.1 SP3 - 發生某些語句故障時,連接池的語句緩存會遺漏關閉語句的操作,進而導致 DBMS 會話中發生游標泄露。為解決此問題,已在 WLS 8.1 SP4 中做了改動。
  • WLS 8.1 SP3 (English) 中對用於實現 XA 語句緩存的底層回收對象進行了修正。
  • CR188814 - WLS 8.1 SP2 - 使用 EJB 2.0 CMP 時,更新 BLOB 或 CLOB 列后未關閉 PreparedStatement 和 ResultSet。WebLogic Server 現在通過 WLS 8.1 SP4 實現了在更新 BLOB/CLOB 時關閉 PreparedStatement 和 ResultSet。
使用搜索功能也可以搜索到“Release Notes”,還可以搜索到其它支持解決辦法及與 CR 有關的信息,如需要更多幫助?中所提到的內容。如果客戶簽訂了技術支持合同,則可以登錄 http://support.bea.com/,登錄后會看到為 Solutions 和 Bug Central 提供的 Browse portlet,可在其中按產品版本瀏覽最新提供的 CR。


免責聲明!

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



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