web-dev-qa-db-ja.com

java.sql.SQLException: - ORA-01000:最大オープン・カーソル数を超えました

ORA-01000 SQL例外が発生しました。だから私はそれに関連するいくつかのクエリがあります。

  1. 最大オープンカーソル数は、JDBC接続数に正確に関連していますか。それとも、単一の接続に対して作成したステートメントおよび結果セットオブジェクトに関連していますか。 (接続のプールを使用しています)
  2. データベース内のステートメント/結果セットオブジェクトの数を(接続のように)構成する方法はありますか?
  3. シングルスレッド環境では、メソッドローカルステートメント/結果セットオブジェクトの代わりにインスタンス変数ステートメント/結果セットオブジェクトを使用することをお勧めしますか。
  4. ループ内で準備済みステートメントを実行すると、この問題が発生しますか? (もちろん、私はsqlBatchを使ったかもしれません)注意:pStmtはループが終わると閉じられます。

    { //method try starts  
      String sql = "INSERT into TblName (col1, col2) VALUES(?, ?)";
      pStmt = obj.getConnection().prepareStatement(sql);
      pStmt.setLong(1, subscriberID);
      for (String language : additionalLangs) {
        pStmt.setInt(2, Integer.parseInt(language));
        pStmt.execute();
      }
    } //method/try ends
    
    { //finally starts
       pStmt.close()
    } //finally ends 
    
  5. Conn.createStatement()とconn.prepareStatement(sql)が単一の接続オブジェクトで複数回呼び出されるとどうなりますか?

編集1: 6. Weak/Soft参照文オブジェクトの使用は、漏洩防止に役立ちますか?

編集2: 1.プロジェクトに見つからない "statement.close()"をすべて見つけることができる方法はありますか。私はそれがメモリリークではないことを理解しています。しかし、ガベージコレクションに適したステートメント参照(close()が実行されない場合)を見つける必要がありますか?利用できるツールはありますか?それとも手動で分析する必要がありますか?

私がそれを理解するのを手伝ってください。

溶液

Oracle DBでusername -VELUのオープンされたカーソルを見つけるには

ORALCEマシンに移動して、sysdbaとしてsqlplusを起動します。

[Oracle@db01 ~]$ sqlplus / as sysdba 

それから走りなさい

SELECT   A.VALUE,
    S.USERNAME,
    S.SID,
    S.SERIAL#
  FROM V$SESSTAT A,
    V$STATNAME B,
    V$SESSION S
  WHERE A.STATISTIC# = B.STATISTIC#
    AND S.SID        = A.SID
    AND B.NAME       = 'opened cursors current'
    AND USERNAME     = 'VELU';

できれば最後に私の答えを読んでください。

106

最大オープンカーソル数エラーであるORA-01000は、Oracleデータベース開発では非常に一般的なエラーです。 Javaのコンテキストでは、アプリケーションがデータベースインスタンスに設定されているカーソルよりも多くのResultSetを開こうとすると発生します。

一般的な原因は次のとおりです。

  1. 設定ミス

    • データベース上のカーソルよりも多くのスレッドがデータベースに照会しています。 1つのケースは、データベース上のカーソル数よりも大きい接続とスレッドプールがある場合です。
    • 多くの開発者やアプリケーションが同じDBインスタンス(おそらく多くのスキーマを含む)に接続されており、一緒に使用している接続数が多すぎます。
    • 解決策:

      • カーソルの数を増やす (リソースが許す限り)データベース上
      • アプリケーション内のスレッド数を減らします。
  2. カーソルリーク

    • アプリケーションがResultSet(JDBCの場合)またはカーソル(データベースのストアドプロシージャの場合)を閉じていません。
    • 解決策:カーソルリークはバグです。 DB上のカーソルの数を増やすと、避けられない失敗を単に遅らせることができます。リークは、 静的コード分析JDBC またはアプリケーションレベルのログ記録、および データベースモニタリング を使用して確認できます。

バックグラウンド

この節では、カーソルの背後にある理論とJDBCの使用方法について説明します。背景を知る必要がない場合は、これを飛ばして 'Eliminating Leaks'に直接進んでください。

カーソルとは

カーソルは、クエリの状態、具体的には読者がResultSet内にいる位置を保持するデータベース上のリソースです。各SELECT文にはカーソルがあり、PL/SQLストアドプロシージャは必要な数のカーソルを開いて使用できます。 Orafaq でカーソルについてもっと知ることができます。

データベースインスタンスは通常、いくつかの異なるスキーマ、多くの異なるユーザー、それぞれ複数セッションを提供します。これを行うために、すべてのスキーマ、ユーザー、およびセッションで使用可能な固定数のカーソルがあります。すべてのカーソルが開いていて(使用中)、新しいカーソルを必要とする要求が発生した場合、要求はORA-010000エラーで失敗します。

カーソル数の確認と設定

この番号は通常、インストール時にDBAによって設定されます。現在使用中のカーソルの数、最大数、および構成は、 Oracle SQL Developer の管理者機能でアクセスできます。 SQLからは次のように設定できます。

ALTER SYSTEM SET OPEN_CURSORS=1337 SID='*' SCOPE=BOTH;

JVM内のJDBCとDB上のカーソルとの関連付け

以下のJDBCオブジェクトは、以下のデータベースの概念と密接に関連しています。

  • JDBC Connectionはデータベースsessionのクライアント表現であり、データベースtransactionsを提供します。 1つの接続で同時に開くことができるトランザクションは1つだけです(ただし、トランザクションはネストできます)。
  • JDBC ResultSetは、データベース上の単一のcursorでサポートされています。 ResultSetでclose()が呼び出されると、カーソルは解放されます。
  • JDBC CallableStatementは、データベース上でストアドプロシージャを呼び出します。通常、PL/SQLで記述されます。ストアード・プロシージャーはゼロ個以上のカーソルを作成でき、カーソルをJDBC ResultSetとして戻すことができます。

JDBCはスレッドセーフです。スレッド間でさまざまなJDBCオブジェクトを受け渡しても大丈夫です。

たとえば、1つのスレッドで接続を作成できます。別のスレッドがこの接続を使用してPreparedStatementを作成し、3番目のスレッドが結果セットを処理できます。唯一の大きな制限は、1つのPreparedStatementで複数のResultSetを同時に開くことができないことです。 Oracle DBは、接続ごとに複数の(パラレル)操作をサポートしますか? を参照してください。

データベースのコミットは接続上で行われるため、その接続上のすべてのDML(INSERT、UPDATEおよびDELETE)は一緒にコミットされます。したがって、同時に複数のトランザクションをサポートする場合は、各同時トランザクションに対して少なくとも1つの接続が必要です。

JDBCオブジェクトを閉じる

ResultSetを実行する典型的な例は次のとおりです。

Statement stmt = conn.createStatement();
try {
    ResultSet rs = stmt.executeQuery( "SELECT FULL_NAME FROM EMP" );
    try {
        while ( rs.next() ) {
            System.out.println( "Name: " + rs.getString("FULL_NAME") );
        }
    } finally {
        try { rs.close(); } catch (Exception ignore) { }
    }
} finally {
    try { stmt.close(); } catch (Exception ignore) { }
}

Finally節は、close()によって発生した例外を無視することに注意してください。

  • Try {} catch {}を使わずに単純にResultSetを閉じると、失敗してStatementが閉じられなくなる可能性があります。
  • 試行の本体で発生した例外を呼び出し元に伝播することを許可します。文の作成や実行など、ループがある場合は、ループ内の各文を閉じることを忘れないでください。

Java 7では、Oracleは AutoCloseableインタフェース を導入しました。これは、Java 6の定型句の大部分をいくつかのNice構文糖で置き換えます。

JDBCオブジェクトの保持

JDBCオブジェクトは、ローカル変数、オブジェクトインスタンス、およびクラスメンバに安全に保持できます。一般的には、次のことをお勧めします。

  • ConnectionsやPreparedStatementsなど、長期間にわたって複数回再利用されるJDBCオブジェクトを保持するには、オブジェクトインスタンスまたはクラスメンバーを使用します。
  • ResultSetにはローカル変数を使用してください。これらは、通常は単一の関数の範囲内で取得、ループオーバー、そしてクローズされるためです。

ただし、例外が1つあります。EJB、またはサーブレット/ JSPコンテナを使用している場合は、厳密なスレッドモデルに従う必要があります。

  • Application Serverだけがスレッドを作成します(スレッドを使用して着信要求を処理します)
  • Application Serverのみが接続を作成します(接続プールから取得します)。
  • 呼び出しの間に値(状態)を保存するときは、細心の注意を払う必要があります。自分のキャッシュや静的メンバーに値を格納しないでください。これはクラスタやその他の奇妙な状況では安全ではありません。また、Application Serverはあなたのデータに恐ろしいことをするかもしれません。代わりに、ステートフルBeanまたはデータベースを使用してください。
  • 特に、neverは、異なるリモート呼び出しでJDBCオブジェクト(Connections、ResultSet、PreparedStatementsなど)を保持します - Application Serverにこれを管理させます。 Application Serverは接続プールを提供するだけでなく、PreparedStatementsもキャッシュします。

漏れをなくす

JDBCリークの検出と排除に役立つ、利用可能なプロセスとツールがいくつかあります。

  1. 開発中 - 早くバグを捉えることは断然最良の方法です:

    1. 開発慣行:良い開発慣行は、ソフトウェアが開発者の机を離れる前に、ソフトウェアのバグの数を減らすはずです。具体的な方法は次のとおりです。

      1. ペアプログラミング 、十分な経験のない人を教育するために
      2. コードレビュー 多くの目が1つより良いから
      3. 単体テスト つまり、複製を簡単に再現できるようにするテストツールからコードベースの一部または全部を実行できます。
      4. 独自に構築するのではなく、接続プールに 既存のライブラリ を使用する
    2. 静的コード分析:静的コード分析を実行するには、優れた Findbugs のようなツールを使用してください。これはclose()が正しく処理されていない多くの場所を拾います。 FindbugsにはEclipse用のプラグインがありますが、単発のためにスタンドアロンで実行され、Jenkins CIや他のビルドツールに統合されています。

  2. 実行時に

    1. 保持力とコミット

      1. ResultSetの保持機能がResultSet.CLOSE_CURSORS_OVER_COMMITの場合、Connection.commit()メソッドが呼び出されるとResultSetは閉じられます。これは、Connection.setHoldability()またはオーバーロードされたConnection.createStatement()メソッドを使用して設定できます。
    2. 実行時のロギング.

      1. あなたのコードに良いログステートメントを入れてください。顧客、サポートスタッフ、およびチームメイトがトレーニングなしで理解できるように、これらは明確かつ理解可能であるべきです。それらは簡潔で、処理ロジックをトレースできるようにキー変数と属性の状態/内部値を表示することを含むべきです。適切なロギングは、アプリケーション、特にデプロイされているアプリケーションをデバッグするための基本です。
      2. デバッグ用のJDBCドライバをプロジェクトに追加できます(デバッグ用 - 実際にはデプロイしないでください)。一例(私はそれを使ったことがない)は log4jdbc です。次に、このファイルを簡単に分析して、どの実行に対応するクローズがないかを確認する必要があります。潜在的な問題がある場合は、開始と終了を数えることが強調表示されます。

        1. データベースの監視SQL Developerの「SQLの監視」機能や QuestのTOAD などのツールを使用して、実行中のアプリケーションを監視します。監視については この記事 に説明があります。監視中に、開いているカーソル(たとえば、テーブルv $ sesstatから)をクエリし、それらのSQLを確認します。カーソルの数が増え、(最も重要なこととして)1つの同一のSQL文によって支配されるようになった場合、そのSQLにはリークがあることがわかります。コードを検索して確認します。

他の考え

あなたはWeakReferencesを使って接続を閉じることができますか?

弱い参照と弱い参照は、適切であると判断されたときにいつでもJVMが参照先をガベージコレクションできるようにオブジェクトを参照できるようにする方法です(そのオブジェクトへの強い参照チェーンがないと仮定します)。

コンストラクタでReferenceQueueを弱い参照または弱い参照に渡すと、オブジェクトが発生したときにGCが実行されたときに(まったく発生した場合)、オブジェクトはReferenceQueueに配置されます。この方法では、オブジェクトのファイナライズと対話し、その時点でオブジェクトを閉じるかファイナライズすることができます。

ファントム参照は少し奇妙です。その目的はファイナライズを制御することだけですが、元のオブジェクトへの参照を取得することはできないので、close()メソッドを呼び出すのは困難になります。

ただし、GCを実行するタイミングを制御することを試みることはめったにありません(Weak、Soft、およびPhantomReferencesは、オブジェクトがGCのためにキューに入れられたことをafter after factに知らせます)。実際、JVMのメモリ容量が大きい場合(例:-Xmx2000m)、オブジェクトをnever GCすることがありますが、それでもORA-01000が発生します。 JVMメモリがプログラムの要件に比べて小さい場合は、ResultSetオブジェクトとPreparedStatementオブジェクトが作成直後(それらから読み取ることができる前)にGCされている可能性があります。これはプログラムに失敗する可能性があります。

TL; DR:弱い参照メカニズムは、StatementオブジェクトとResultSetオブジェクトを管理して閉じるための良い方法ではありません。

274
Andrew Alcock

もう少し理解を深めています。

  1. カーソルはステートメントオブジェクトに関するものだけです。 resultSetでも接続オブジェクトでもありません。
  2. それでも、Oracleメモリを解放するために結果セットを閉じる必要があります。それでもあなたがCURSORSのために数えられない結果セットを閉じないならば。
  3. Statementオブジェクトを閉じると、結果セットオブジェクトも自動的に閉じます。
  4. カーソルはすべてのSELECT/INSERT/UPDATE/DELETEステートメントに対して作成されます。
  5. 各Oracle DBインスタンスは、Oracle SIDを使用して識別できます。同様に、Oracle DBは接続SIDを使用して各接続を識別できます。両方のSIDが異なります。
  6. つまり、Oracleセッションはjdbc(tcp)接続に他なりません。これは1つのSIDにすぎません。
  7. 最大カーソル数を500に設定した場合、それは1つのJDBCセッション/接続/ SIDに対してのみです。
  8. そのため、それぞれのno of cursor(ステートメント)とJDBC接続を多数持つことができます。
  9. JVMが終了すると、すべての接続/カーソルが閉じられ、OR JDBCConnectionが閉じられ、その接続に関するCURSORSが閉じられます。

Sysdbaとしてログインします。

PuTTY(Oracleログイン)の場合:

  [Oracle@db01 ~]$ sqlplus / as sysdba

SqlPlusの場合:

ユーザー名:sys as sysdba

Session_cached_cursors値を0に設定して、カーソルが閉じていないようにします。

 alter session set session_cached_cursors=0
 select * from V$PARAMETER where name='session_cached_cursors'

DB内の接続ごとに既存のOPEN_CURSORS値セットを選択します。

 SELECT max(a.value) as highest_open_cur, p.value as max_open_cur FROM v$sesstat a, v$statname b, v$parameter p WHERE a.statistic# = b.statistic# AND b.name = 'opened cursors current' AND p.name= 'open_cursors'  GROUP BY p.value;

以下は、開いているカーソル値を含むSID /接続リストを見つけるためのクエリです。

 SELECT a.value, s.username, s.sid, s.serial#
 FROM v$sesstat a, v$statname b, v$session s
 WHERE a.statistic# = b.statistic#  AND s.sid=a.sid 
 AND b.name = 'opened cursors current' AND username = 'SCHEMA_NAME_IN_CAPS'

以下のクエリを使用して、開いているカーソル内のSQLを識別します。

 SELECT oc.sql_text, s.sid 
 FROM v$open_cursor oc, v$session s
 WHERE OC.sid = S.sid
 AND s.sid=1604
 AND OC.USER_NAME ='SCHEMA_NAME_IN_CAPS'

今すぐコードをデバッグしてお楽しみください! :)

25

このようにコードを修正してください。

try
{ //method try starts  
  String sql = "INSERT into TblName (col1, col2) VALUES(?, ?)";
  pStmt = obj.getConnection().prepareStatement(sql);
  pStmt.setLong(1, subscriberID);
  for (String language : additionalLangs) {
    pStmt.setInt(2, Integer.parseInt(language));
    pStmt.execute();
  }
} //method/try ends
finally
{ //finally starts
   pStmt.close()
} 

本当に、あなたは本当にあなたのpStatement、接続および結果を閉じているということですか?

開いているオブジェクトを分析するには、ステートマント、接続、および結果オブジェクトをコードで囲む委任パターンを暗黙的に指定します。そのため、オブジェクトが正常に閉じられるかどうかがわかります。

PStmt = obj .getConnection()。prepareStatement(sql);の例

    class obj{ 

    public Connection getConnection(){
    return new ConnectionDelegator(...here create your connection object and put it into ...);

    } 
}


class ConnectionDelegator implements Connection{
    Connection delegates;

    public ConnectionDelegator(Connection con){
       this.delegates = con;
    }

    public Statement prepareStatement(String sql){
        return delegates.prepareStatement(sql);
    }

    public void close(){
        try{
           delegates.close();
        }finally{
           log.debug(delegates.toString() + " was closed");
        }
    }
}
4
Mirko

アプリケーションがアプリケーションサーバーとしてOracle WebLogicで実行されているJava EEアプリケーションである場合、この問題の考えられる原因はWebLogicの Statement Cache Size 設定です。

特定のデータソースの文キャッシュサイズの設定がOracleデータベースの最大オープンカーソル数の設定とほぼ同じかそれより大きい場合、WebLogicによってオープンに保持されているキャッシュされたSQL文によってすべてのオープンカーソルが消費されます。 ORA-01000エラーにあります。

これに対処するには、Oracleデータベースを指す各WebLogicデータソースのStatement Cache Size設定を、データベースの最大カーソル数設定よりも大幅に少なくします。

WebLogic 10アドミンコンソールでは、各データソースのStatement Cache Size設定は、[サービス](左側のナビゲーション)> [データソース]>(個々のデータソース)> [接続プール]タブで確認できます。

3
Jon Schneider

今日も同じ問題(ORA-01000)に直面しました。 try {}内にforループを使用して、Oracle DB内でSELECT文を何度も(パラメータを変更するたびに)実行し、最後の{}内には、いつものようにResultset、PreparedStatementおよびConnectionを閉じるコードがあります。 。しかし、特定のループ数(1000)に達するとすぐに、オープンカーソルが多すぎるというOracleエラーが発生しました。

上記のAndrew Alcockによる投稿に基づいて、insideループのように、データを取得した後、再度ループする前に各結果セットと各文を閉じて、問題を解決しました。

さらに、今回と同様に、別のOracle DB(ORA-01000)のInsert文の別のループでも、まったく同じ問題が発生しました。今回は300文の後です。これも同じ方法で解決されたため、PreparedStatementまたはResultSet、あるいはその両方が、クローズされるまでオープンカーソルとしてカウントされます。

2
Kinnison84

私もこの問題に直面していました。

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

DaoレイヤにSpring Framework with Spring JDBCを使っていました。

私のアプリケーションはどういうわけかカーソルをリークし、数分かそこら後に、それは私にこの例外を与えていました。

たくさんの徹底的なデバッグと分析の後、私はTableのうちの1つでIndexing、Primary Key and Unique Constraintsに問題があることを知りました - クエリ私は実行していました。

私のアプリケーションはColumnsを誤って更新しようとしていましたIndexed。そのため、私のアプリケーションがインデックス付きカラムに対してupdateクエリを実行しているときはいつでも、データベースは更新された値に基づいてインデックスの再作成を試みていました。 カーソルをリークしていました。

クエリで検索するのに使用された列に適切なインデックスを作成し、そして必要に応じて適切な制約を適用することで問題を解決することができました。

2
Piyush Verma

開かれたSQLを見つけるためのクエリ。

SELECT s.machine, oc.user_name, oc.sql_text, count(1) 
FROM v$open_cursor oc, v$session s
WHERE oc.sid = s.sid
and S.USERNAME='XXXX'
GROUP BY user_name, sql_text, machine
HAVING COUNT(1) > 2
ORDER BY count(1) DESC
1
Hlex

Autocommit = trueを設定しましたか?そうでなければこれを試してください:

{ //method try starts  
    String sql = "INSERT into TblName (col1, col2) VALUES(?, ?)";
    Connection conn = obj.getConnection()
    pStmt = conn.prepareStatement(sql);

    for (String language : additionalLangs) {
        pStmt.setLong(1, subscriberID);
        pStmt.setInt(2, Integer.parseInt(language));
        pStmt.execute();
        conn.commit();
    }
} //method/try ends { 
    //finally starts
    pStmt.close()
} //finally ends 
1
paweloque

今回のケースでは、Hibernateを使用していて、同じHibernateマップエンティティを参照する多くの変数がありました。これらの参照を作成してループで保存していました。各参照はカーソルを開き、開いたままにしました。

コードの実行中に 開いているカーソルの数をチェックするためのクエリ を使用し、デバッガを使用して選択的にコメントアウトすることでこれを発見しました。

それぞれの新しい参照が別のカーソルを開く理由について - 問題のエンティティはそれにマッピングされた他のエンティティのコレクションを持っていました、そして私はこれと関係があると思います(おそらくこれだけではなくフェッチモードとキャッシュ設定)。 Hibernate自体は クローズに失敗することに関するバグ 開いているカーソルを持っていました、これらは後のバージョンで修正されたように見えますが。

とにかく同じエンティティに対してそれほど多くの重複する参照を持つ必要はないので、解決策はそれらの冗長な参照をすべて作成して保持するのをやめることでした。いったん私たちがその問題をやめたとき。

0
dshiga

私は、Oracle 10gに接続しているWildFlyとTomcatのデータソースでこの問題を抱えていました。

特定の条件下で、statement.close()が呼び出されてもステートメントが閉じられないことがわかりました。問題は、使用しているOracleドライバ、ojdbc7.jarにあります。このドライバはOracle 12cおよび11gを対象としており、Oracle 10gで使用するといくつか問題があるように思われるので、私はojdbc5.jarにダウングレードしてもすべて正常に動作しています。

0
gilbertoag

1000回以上の繰り返しでdbにクエリを実行していたため、同じ問題に直面しました。私はtryを使用し、最後に私のコードで使用しました。しかし、まだエラーを受けていました。

これを解決するために、私はOracle dbにログインして、以下のクエリを実行しました。

ALTER SYSTEM SET open_cursors = 8000 SCOPE = BOTH;

そして、これで私の問題はすぐに解決しました。

0
RArora

この問題は主に接続プールを使用している場合に発生します。接続を閉じると、その接続は接続プールに戻り、データベースへの接続が開いたままであるため、その接続に関連付けられているカーソルがすべて閉じられることはありません。そのため、プール内の接続のアイドル接続時間を短縮することも考えられます。接続がアイドル状態のまま10秒間待機するたびに、データベースへの接続が閉じられ、新しい接続が作成されてプールに入れられます。

0
Raveesh Badoni

バッチ処理を使用すると、オーバーヘッドが少なくなります。例については、次のリンクを参照してください。 http://www.tutorialspoint.com/jdbc/jdbc-batch-processing.htm

0
Manjunath