診斷查詢 Oracle 使用 init.ora 中的初始化參數 OPEN_CURSORS 指定一個會話一次最多可以擁有的游標數。缺省值為 50。遺憾的是,此缺省值通常對 WebLogic Server 這樣的系統來說過小。要獲得數據庫中 OPEN_CURSORS 參數的值,可以使用以下查詢: |
SQL> show parameter open_cursors;
NAME TYPE VALUE |
重要的是將 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 |
結果將顯示正在連接上執行的查詢。它提供了一個入手點,讓您可以反向跟蹤到打開游標的來源。返回頁首 常見成因及解決辦法 此問題的最常見成因是未正常關閉 JDBC 對象。使用診斷查詢中第三個查詢的結果在應用程序代碼中反向跟蹤,確保將所有 JDBC 對象都正常關閉。BEA 建議在 finally 塊中顯式關閉 Connection、Statement 和 ResultSet 等 JDBC 對象,以確保無論是在正常還是異常情況下都將所有 JDBC 對象關閉。下面是一個常規示例: |
Connection conn = null; Statement stmt = null; ResultSet rs = null; try { |
請避免采用任何放棄 JDBC 對象的代碼慣例。下面的代碼慣例在每個循環迭代中都獲得一個新的 Connection、Statement 和 ResultSet,但它沒有關閉每個迭代的 JDBC 對象。因此,它會導致 JDBC 對象泄漏。 |
Connection conn = null; Statement stmt = null; ResultSet rs = null; String[] queries = new String[10]; //Define queries try { |
盡管根據 JDBC 規范的規定,關閉 Connection 時正常情況下也會將 Statement 和 ResultSet 關閉,但好的做法是:如果在一個 Connection 對象上創建了多個 Statement,則在使用完 Statement 和 ResultSet 后立即顯式將它們關閉。如果未立即顯式關閉 Statement 和 ResultSet,游標可能會積聚并在關閉 Connection 前超過數據庫允許的最大數量。例如,在以下代碼片斷中,正常情況下通過 finally 塊關閉 Connection 時,也會將 ResultSet 和 Statement 關閉。不過,此代碼片斷在一個連接上創建了多個 Statement 和 ResultSet。因此在循環完成前,可能已發生“超出最多允許打開的游標數”問題。 |
Connection conn = null; try{ for(int i = 0; i < NUM_STMT; i++) { |
返回頁首 語句緩存 為提高性能,WebLogic Server 提供了一種功能,讓您可以在使用連接池時將預處理語句和可調用語句載入緩存。當 WebLogic Server 將預處理語句或可調用語句載入緩存時,在許多情況下,DBMS 將為每個打開的語句都保留游標。因此,語句緩存可能是“超出最多允許打開的游標數”問題的成因。“語句緩存大小”屬性決定在每個連接池實例中為每個連接緩存的預處理和可調用語句的總數。如果緩存的語句過多,可能會導致超過數據庫服務器上打開游標數的上限。 請注意,各版本 WebLogic Server 的缺省語句緩存大小是有差異的。示例:
要確定“超出最多允許打開的游標數”問題是否與語句緩存有關,可以通過將語句緩存大小設置為 0 將此功能關閉或減少緩存大小,再確認是否仍會出現錯誤。如果在減少緩存大小后問題沒有發生,則說明連接池原有的語句緩存過大或 DBMS 中打開游標數的上限過低。可能需要考慮調整其中的一個值。如果發現連接上打開的游標數持續增加,但在將語句緩存大小設置為 0 后沒有出現這種現象,則可能說明存在游標泄漏問題。這可能是由使用的 JDBC 驅動程序所致,也可能是 WebLogic Server 本身的一個錯誤。請嘗試使用其它 JDBC 驅動程序。如果使用其它 JDBC 驅動程序后仍發生同樣的問題,請將此問題報告給 BEA,這樣支持工程師可以對問題做進一步探查,以確定該問題是否為 WebLogic Server 自身的一個錯誤。 數據庫驅動程序 1. 直接從驅動程序獲取連接。 可以嘗試使用其它供應商的 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 驅動程序錯誤。 如果問題是 JDBC 驅動程序問題,但又不得不使用該驅動程序,一種以變通方式解決游標泄漏問題的方法是不時重設 WebLogic 連接池,或收縮連接池。有關重設或收縮連接池的方法,請參閱 WebLogic 文檔(如果是 8.1 版本,該文檔位于 http://e-docs.bea.com/wls/docs81/ConsoleHelp/domain_jdbcconnectionpool_control.html (Enlish))。 |