java.sql.SQLException: - ORA-01000: Maximale Anzahl offener Cursor überschritten

115

Ich erhalte eine ORA-01000 SQL-Ausnahme. Ich habe also einige Fragen dazu.

  1. Beziehen sich die maximal geöffneten Cursor genau auf die Anzahl der JDBC-Verbindungen oder auch auf die Anweisungs- und Ergebnismengenobjekte, die wir für eine einzelne Verbindung erstellt haben? (Wir verwenden Verbindungspool)
  2. Gibt es eine Möglichkeit, die Anzahl der Anweisungs- / Ergebnismengenobjekte in der Datenbank (wie Verbindungen) zu konfigurieren?
  3. Ist es ratsam, die Instanzvariablen Anweisung / Ergebnismenge Objekt anstelle der Methode lokale Anweisung / Ergebnismenge Objekt in einer Umgebung mit einem einzigen Thread zu verwenden?
  4. Verursacht das Ausführen einer vorbereiteten Anweisung in einer Schleife dieses Problem? (Natürlich hätte ich auch sqlBatch verwenden können.) Hinweis: pStmt wird geschlossen, sobald die Schleife beendet ist.

    { //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. Was passiert, wenn conn.createStatement () und conn.prepareStatement (sql) für ein einzelnes Verbindungsobjekt mehrmals aufgerufen werden?

Edit1: 6. Hilft die Verwendung des Referenzobjekts Weak / Soft bei der Verhinderung von Leckagen?

Edit2: 1. Kann ich auf irgendeine Weise alle fehlenden "statement.close ()" in meinem Projekt finden? Ich verstehe, dass es kein Speicherverlust ist. Aber ich muss eine Anweisungsreferenz finden (wo close () nicht ausgeführt wird), die für die Speicherbereinigung geeignet ist. Irgendein Werkzeug verfügbar? Oder muss ich es manuell analysieren?

Bitte helfen Sie mir, es zu verstehen.

Lösung

So finden Sie den geöffneten Cursor in Oracle DB für den Benutzernamen -VELU

Gehen Sie zur ORACLE-Maschine und starten Sie sqlplus als sysdba.

[oracle@db01 ~]$ sqlplus / as sysdba 

Dann renne

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';

Wenn möglich, lesen Sie bitte meine Antwort, um meine Lösung besser zu verstehen

Kanagavelu Sugumar
quelle
Können Sie Ihren vollständigen Code veröffentlichen? Es wäre interessant zu sehen, wo Sie die Öffnungsklammern schließen, die fürfor (String language : additionalLangs) {
Jåcob
@ Kanagavelu Sugumar: Warum nicht 5 verschiedene Fragen in SO stellen?
Jayan
1
Hier ist eine Antwort, die ich sehr nützlich fand: stackoverflow.com/a/4507507/501113
chaotic3quilibrium
Bitte sehen Sie, ob die Antwort nützlich ist: stackoverflow.com/questions/34716456/…
Manu
Wenn Sie offene Cursor in Oracle suchen möchten, sollten Sie sich auch die SYS.V$OPEN_CURSORAnsicht ansehen. Dadurch erhalten Sie nicht nur die SID, sondern auch den SQL-Text.
Bass

Antworten:

290

ORA-01000, der Fehler mit maximal offenen Cursorn, ist ein äußerst häufiger Fehler bei der Entwicklung von Oracle-Datenbanken. Im Kontext von Java tritt dies auf, wenn die Anwendung versucht, mehr ResultSets zu öffnen, als auf einer Datenbankinstanz Cursor konfiguriert sind.

Häufige Ursachen sind:

  1. Konfigurationsfehler

    • Ihre Anwendung hat mehr Threads, die die Datenbank abfragen, als Cursor in der Datenbank. Ein Fall ist, wenn Sie einen Verbindungs- und Thread-Pool haben, der größer ist als die Anzahl der Cursor in der Datenbank.
    • Sie haben viele Entwickler oder Anwendungen, die mit derselben DB-Instanz verbunden sind (die wahrscheinlich viele Schemas enthalten wird), und zusammen verwenden Sie zu viele Verbindungen.
    • Lösung:

  2. Cursorleck

    • Die Anwendungen schließen weder ResultSets (in JDBC) noch Cursor (in gespeicherten Prozeduren in der Datenbank).
    • Lösung : Cursorlecks sind Fehler. Das Erhöhen der Anzahl der Cursor in der Datenbank verzögert lediglich den unvermeidlichen Fehler. Lecks können mithilfe statischer Code-Analyse , JDBC- oder Protokollierung auf Anwendungsebene und Datenbanküberwachung gefunden werden .

Hintergrund

Dieser Abschnitt beschreibt einige der Theorien hinter Cursorn und wie JDBC verwendet werden sollte. Wenn Sie den Hintergrund nicht kennen müssen, können Sie dies überspringen und direkt zu "Beseitigung von Lecks" gehen.

Was ist ein Cursor?

Ein Cursor ist eine Ressource in der Datenbank, die den Status einer Abfrage enthält, insbesondere die Position, an der sich ein Leser in einem ResultSet befindet. Jede SELECT-Anweisung verfügt über einen Cursor, und gespeicherte PL / SQL-Prozeduren können so viele Cursor öffnen und verwenden, wie sie benötigen. Auf Orafaq erfahren Sie mehr über Cursor .

Eine Datenbankinstanz bedient normalerweise mehrere verschiedene Schemas , viele verschiedene Benutzer mit jeweils mehreren Sitzungen . Zu diesem Zweck steht eine feste Anzahl von Cursorn für alle Schemas, Benutzer und Sitzungen zur Verfügung. Wenn alle Cursor geöffnet sind (verwendet werden) und eine Anforderung eingeht, für die ein neuer Cursor erforderlich ist, schlägt die Anforderung mit einem ORA-010000-Fehler fehl.

Suchen und Einstellen der Anzahl der Cursor

Die Nummer wird normalerweise vom DBA bei der Installation konfiguriert. Auf die Anzahl der derzeit verwendeten Cursor, die maximale Anzahl und die Konfiguration kann in den Administratorfunktionen in Oracle SQL Developer zugegriffen werden . In SQL kann Folgendes festgelegt werden:

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

Verknüpfen von JDBC in der JVM mit Cursorn in der Datenbank

Die folgenden JDBC-Objekte sind eng mit den folgenden Datenbankkonzepten verbunden:

  • JDBC - Verbindung ist die Client - Darstellung einer Datenbank - Sitzung und bietet Datenbanktransaktionen . Bei einer Verbindung kann jeweils nur eine Transaktion geöffnet sein (Transaktionen können jedoch verschachtelt sein).
  • Ein JDBC ResultSet wird von einem einzelnen Cursor in der Datenbank unterstützt. Wenn close () im ResultSet aufgerufen wird, wird der Cursor losgelassen.
  • Ein JDBC CallableStatement ruft eine gespeicherte Prozedur in der Datenbank auf, die häufig in PL / SQL geschrieben ist. Die gespeicherte Prozedur kann null oder mehr Cursor erstellen und einen Cursor als JDBC ResultSet zurückgeben.

JDBC ist threadsicher: Es ist völlig in Ordnung, die verschiedenen JDBC-Objekte zwischen Threads zu übergeben.

Beispielsweise können Sie die Verbindung in einem Thread erstellen. Ein anderer Thread kann diese Verbindung verwenden, um ein PreparedStatement zu erstellen, und ein dritter Thread kann die Ergebnismenge verarbeiten. Die einzige wichtige Einschränkung besteht darin, dass nicht mehr als ein ResultSet gleichzeitig für ein einzelnes PreparedStatement geöffnet sein kann. Siehe Unterstützt Oracle DB mehrere (parallele) Vorgänge pro Verbindung?

Beachten Sie, dass ein Datenbank-Commit für eine Verbindung erfolgt und daher alle DMLs (INSERT, UPDATE und DELETE) für diese Verbindung zusammen festgeschrieben werden. Wenn Sie mehrere Transaktionen gleichzeitig unterstützen möchten, müssen Sie daher für jede gleichzeitige Transaktion mindestens eine Verbindung haben.

JDBC-Objekte schließen

Ein typisches Beispiel für die Ausführung eines ResultSet ist:

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) { }
}

Beachten Sie, wie die finally-Klausel jede durch close () ausgelöste Ausnahme ignoriert:

  • Wenn Sie das ResultSet einfach ohne try {} catch {} schließen, schlägt dies möglicherweise fehl und verhindert, dass die Anweisung geschlossen wird
  • Wir möchten zulassen, dass jede Ausnahme, die im Hauptteil des Versuchs ausgelöst wird, an den Anrufer weitergegeben wird. Wenn Sie eine Schleife haben, z. B. Anweisungen erstellen und ausführen, denken Sie daran, jede Anweisung innerhalb der Schleife zu schließen.

In Java 7 hat Oracle die AutoCloseable-Oberfläche eingeführt, die den größten Teil der Java 6-Boilerplate durch einen schönen syntaktischen Zucker ersetzt.

JDBC-Objekte halten

JDBC-Objekte können sicher in lokalen Variablen, Objektinstanzen und Klassenmitgliedern gespeichert werden. Es ist im Allgemeinen besser zu praktizieren:

  • Verwenden Sie Objektinstanz- oder Klassenmitglieder, um JDBC-Objekte zu speichern, die über einen längeren Zeitraum mehrmals wiederverwendet werden, z. B. Connections und PreparedStatements
  • Verwenden Sie lokale Variablen für ResultSets, da diese normalerweise im Rahmen einer einzelnen Funktion abgerufen, durchlaufen und dann geschlossen werden.

Es gibt jedoch eine Ausnahme: Wenn Sie EJBs oder einen Servlet / JSP-Container verwenden, müssen Sie einem strengen Threading-Modell folgen:

  • Nur der Anwendungsserver erstellt Threads (mit denen er eingehende Anforderungen verarbeitet).
  • Nur der Anwendungsserver erstellt Verbindungen (die Sie aus dem Verbindungspool erhalten).
  • Beim Speichern von Werten (Status) zwischen Aufrufen müssen Sie sehr vorsichtig sein. Speichern Sie niemals Werte in Ihren eigenen Caches oder statischen Mitgliedern - dies ist in Clustern und anderen seltsamen Bedingungen nicht sicher, und der Anwendungsserver kann schreckliche Daten an Ihren Daten anrichten. Verwenden Sie stattdessen Stateful Beans oder eine Datenbank.
  • Insbesondere nie halten JDBC - Objekte (Verbindungen, Result, PreparedStatements, usw.) über verschiedene Remote - Anrufungen - lassen Sie den Application Server diese verwalten. Der Anwendungsserver stellt nicht nur einen Verbindungspool bereit, sondern speichert auch Ihre PreparedStatements zwischen.

Leckagen beseitigen

Es gibt eine Reihe von Prozessen und Tools, mit denen JDBC-Lecks erkannt und beseitigt werden können:

  1. Während der Entwicklung ist das frühzeitige Erkennen von Fehlern bei weitem der beste Ansatz:

    1. Entwicklungspraktiken: Gute Entwicklungspraktiken sollten die Anzahl der Fehler in Ihrer Software verringern, bevor sie den Entwickler-Desk verlässt. Spezifische Praktiken umfassen:

      1. Paarprogrammierung , um diejenigen ohne ausreichende Erfahrung auszubilden
      2. Codeüberprüfungen, weil viele Augen besser als eines sind
      3. Unit-Tests , dh Sie können Ihre gesamte Codebasis mit einem Testwerkzeug trainieren, wodurch die Reproduktion von Lecks trivial wird
      4. Verwenden Sie vorhandene Bibliotheken für das Verbindungspooling, anstatt Ihre eigenen zu erstellen
    2. Statische Code-Analyse: Verwenden Sie ein Tool wie die hervorragenden Findbugs , um eine statische Code-Analyse durchzuführen. Dies nimmt viele Stellen auf, an denen close () nicht korrekt behandelt wurde. Findbugs hat ein Plugin für Eclipse, läuft aber auch eigenständig für Einzelstücke und ist in Jenkins CI und andere Build-Tools integriert

  2. Zur Laufzeit:

    1. Haltbarkeit und Engagement

      1. Wenn die ResultSet-Haltbarkeit ResultSet.CLOSE_CURSORS_OVER_COMMIT lautet, wird das ResultSet geschlossen, wenn die Connection.commit () -Methode aufgerufen wird. Dies kann mit Connection.setHoldability () oder mit der überladenen Connection.createStatement () -Methode festgelegt werden.
    2. Protokollierung zur Laufzeit.

      1. Fügen Sie gute Protokollanweisungen in Ihren Code ein. Diese sollten klar und verständlich sein, damit der Kunde, die Support-Mitarbeiter und die Teamkollegen ohne Schulung verstehen können. Sie sollten knapp sein und das Drucken der Status- / internen Werte von Schlüsselvariablen und Attributen umfassen, damit Sie die Verarbeitungslogik verfolgen können. Eine gute Protokollierung ist für das Debuggen von Anwendungen von grundlegender Bedeutung, insbesondere von Anwendungen, die bereitgestellt wurden.
      2. Sie können Ihrem Projekt einen JDBC-Debugging-Treiber hinzufügen (zum Debuggen - stellen Sie ihn nicht bereit). Ein Beispiel (ich habe es nicht verwendet) ist log4jdbc . Anschließend müssen Sie eine einfache Analyse dieser Datei durchführen, um festzustellen, welche Ausführungen keinen entsprechenden Abschluss haben. Das Zählen des Öffnens und Schließens sollte hervorheben, ob ein potenzielles Problem vorliegt

        1. Überwachung der Datenbank. Überwachen Sie Ihre laufende Anwendung mit Tools wie der SQL Developer-Funktion 'SQL überwachen' oder Quest's TOAD . Die Überwachung wird in diesem Artikel beschrieben . Während der Überwachung fragen Sie die geöffneten Cursor ab (z. B. aus der Tabelle v $ sesstat) und überprüfen deren SQL. Wenn die Anzahl der Cursor zunimmt und (am wichtigsten) von einer identischen SQL-Anweisung dominiert wird, wissen Sie, dass Sie ein Leck mit dieser SQL haben. Suchen Sie Ihren Code und überprüfen Sie.

Andere Gedanken

Können Sie WeakReferences verwenden, um das Schließen von Verbindungen zu handhaben?

Schwache und weiche Referenzen ermöglichen es Ihnen, ein Objekt so zu referenzieren, dass die JVM den Referenten jederzeit mit Müll sammeln kann, wenn dies für angemessen erachtet wird (vorausgesetzt, es gibt keine starken Referenzketten für dieses Objekt).

Wenn Sie eine ReferenceQueue im Konstruktor an die weiche oder schwache Referenz übergeben, wird das Objekt in die ReferenceQueue gestellt, wenn das Objekt bei seinem Auftreten GC-geprüft wird (falls es überhaupt auftritt). Mit diesem Ansatz können Sie mit der Finalisierung des Objekts interagieren und das Objekt in diesem Moment schließen oder finalisieren.

Phantomreferenzen sind etwas seltsamer; Ihr Zweck ist nur die Steuerung der Finalisierung, aber Sie können niemals einen Verweis auf das ursprüngliche Objekt erhalten, daher wird es schwierig sein, die close () -Methode darauf aufzurufen.

Es ist jedoch selten eine gute Idee, zu versuchen, zu steuern, wann der GC ausgeführt wird (Schwache, weiche und Phantomreferenzen lassen Sie wissen, nachdem das Objekt für den GC in die Warteschlange gestellt wurde). Wenn der Speicher in der JVM groß ist (z. B. -Xmx2000m), wird das Objekt möglicherweise nie GC-fähig, und der ORA-01000 wird weiterhin angezeigt. Wenn der JVM-Speicher im Verhältnis zu den Anforderungen Ihres Programms klein ist, werden die Objekte ResultSet und PreparedStatement möglicherweise sofort nach der Erstellung (bevor Sie daraus lesen können) überprüft, was wahrscheinlich zu einem Fehler Ihres Programms führen wird.

TL; DR: Der schwache Referenzmechanismus ist keine gute Möglichkeit, Statement- und ResultSet-Objekte zu verwalten und zu schließen.

Andrew Alcock
quelle
3
Wenn Sie Anweisungen in einer Schleife erstellen, stellen Sie sicher, dass diese in der Schleife geschlossen sind. Andernfalls wird nur die letzte Anweisung geschlossen.
Basiljames
Danke, Basiljames. Bearbeiten Sie einfach die Antwort, um den von Ihnen gemachten Punkt hinzuzufügen.
Andrew Alcock
@ Andrew Alcock Vielen Dank! Andrew. Könnten Sie bitte auch den 6. beantworten?
Kanagavelu Sugumar
@ AndrewAlcock Bitte .. bitte .. bitte .. beantworte auch meine 7. Frage. Seit unserem Projekt sind wir beim Auslastungstest sehr häufig mit ORA-01000 konfrontiert. Ihre Beiträge sind für mich wertvoller. Vielen Dank im Voraus!
Kanagavelu Sugumar
RE: 7 - Sie können die Annäherungssuche mit einem Tool wie grep versuchen. Wenn Sie eine SQL erkennen (auswählen, einfügen, aktualisieren, löschen), sehen Sie die Nähe des Wortes close () neben der Anweisung. Wenn die Nähe weiter entfernt ist als erwartet, kann dies eine Möglichkeit sein, zu untersuchen, wo sie fehlt. lightboxtechnologies.com/2012/07/27/…
So
28

Ich füge ein paar weitere Erkenntnisse hinzu.

  1. Der Cursor handelt nur von einem Anweisungsobjekt. Es ist weder resultSet noch das Verbindungsobjekt.
  2. Trotzdem müssen wir die Ergebnismenge schließen, um etwas Orakelspeicher freizugeben. Wenn Sie die Ergebnismenge jedoch nicht schließen, wird sie für CURSORS nicht gezählt.
  3. Durch das Schließen des Anweisungsobjekts wird auch das Ergebnismengenobjekt automatisch geschlossen.
  4. Der Cursor wird für alle Anweisungen SELECT / INSERT / UPDATE / DELETE erstellt.
  5. Jede ORACLE DB-Instanz kann mithilfe der Oracle-SID identifiziert werden. In ähnlicher Weise kann ORACLE DB jede Verbindung anhand der Verbindungs-SID identifizieren. Beide SID sind unterschiedlich.
  6. Die ORACLE-Sitzung ist also nichts anderes als eine jdbc (tcp) -Verbindung. Das ist nichts als eine SID.
  7. Wenn wir die maximale Anzahl von Cursorn auf 500 setzen, gilt dies nur für eine JDBC-Sitzung / Verbindung / SID.
  8. So können wir viele JDBC-Verbindungen mit der jeweiligen Anzahl von Cursorn (Anweisungen) haben.
  9. Sobald die JVM beendet ist, werden alle Verbindungen / Cursor geschlossen, ODER JDBCConnection wird geschlossen. CURSORS in Bezug auf diese Verbindung werden geschlossen.

Loggin als sysdba.

In Putty (Oracle-Login):

  [oracle@db01 ~]$ sqlplus / as sysdba

In SqlPlus:

Nutzername: sys as sysdba

Setzen Sie den Wert für session_cached_cursors auf 0, damit keine Cursor geschlossen werden.

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

Wählen Sie den vorhandenen OPEN_CURSORS-Wertesatz aus, der pro Verbindung in der Datenbank festgelegt wurde

 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;

Unten finden Sie die Abfrage, um die SID / Verbindungsliste mit geöffneten Cursorwerten zu finden.

 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'

Verwenden Sie die folgende Abfrage, um die SQLs in den geöffneten Cursorn zu identifizieren

 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'

Jetzt debuggen Sie den Code und genießen Sie !!! :) :)

Kanagavelu Sugumar
quelle
1
Hier ist eine andere Abfrage, die gut zu funktionieren scheint: stackoverflow.com/a/2560415/32453
Rogerdpack
4

Korrigieren Sie Ihren Code folgendermaßen:

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()
} 

Sind Sie sicher, dass Sie Ihre pStatements, Verbindungen und Ergebnisse wirklich schließen?

Um offene Objekte zu analysieren, können Sie ein Delegatormuster implementieren, das Code um Ihre Statemant-, Verbindungs- und Ergebnisobjekte legt. Sie werden also sehen, ob ein Objekt erfolgreich geschlossen wurde.

Ein Beispiel für: 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");
        }
    }
}
Mirko
quelle
3

Wenn es sich bei Ihrer Anwendung um eine Java EE-Anwendung handelt, die unter Oracle WebLogic als Anwendungsserver ausgeführt wird, ist die Einstellung für die Größe des Anweisungscaches in WebLogic eine mögliche Ursache für dieses Problem .

Wenn die Einstellung für die Größe des Anweisungscaches für eine bestimmte Datenquelle ungefähr gleich oder größer als die Einstellung für die maximale Anzahl offener Cursor in der Oracle-Datenbank ist, können alle offenen Cursor von zwischengespeicherten SQL-Anweisungen verwendet werden, die von WebLogic offen gehalten werden im ORA-01000 Fehler.

Um dies zu beheben, reduzieren Sie die Einstellung für die Größe des Anweisungscaches für jede WebLogic-Datenquelle, die auf die Oracle-Datenbank verweist, so, dass sie erheblich unter der Einstellung für die maximale Cursoranzahl in der Datenbank liegt.

In der WebLogic 10-Administratorkonsole finden Sie die Einstellung für die Größe des Anweisungscaches für jede Datenquelle unter Dienste (linke Navigation)> Datenquellen> (einzelne Datenquelle)> Registerkarte Verbindungspool.

Jon Schneider
quelle
1
Der Ruhezustand verfügt auch über einen Anweisungscache. Siehe auch developer.jboss.org/wiki/…
Pino
3

Auch ich hatte dieses Problem. Die folgende Ausnahme kam früher

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

Ich habe Spring Framework mit Spring JDBC für die Dao-Ebene verwendet.

Meine Anwendung leckte Cursor irgendwie und nach ein paar Minuten gab es mir diese Ausnahme.

Nach vielen gründlichen Debugging- und Analysevorgängen stellte ich fest, dass in einer der Tabellen , die in der von mir ausgeführten Abfrage verwendet wurden, ein Problem mit der Indizierung, dem Primärschlüssel und den eindeutigen Einschränkungen auftrat .

Meine Anwendung hat versucht, die fälschlicherweise indizierten Spalten zu aktualisieren . Wenn meine Anwendung die Aktualisierungsabfrage für die indizierten Spalten traf, versuchte die Datenbank, die Neuindizierung basierend auf den aktualisierten Werten durchzuführen. Es leckte die Cursor .

Ich konnte das Problem lösen, indem ich die Spalten, die zur Suche in der Abfrage verwendet wurden, ordnungsgemäß indizierte und bei Bedarf geeignete Einschränkungen anwendete.

Piyush Verma
quelle
2

Ich hatte heute das gleiche Problem (ORA-01000). Ich hatte eine for-Schleife in try {}, um eine SELECT-Anweisung in einer Oracle-Datenbank viele Male auszuführen (jedes Mal, wenn ein Parameter geändert wurde), und schließlich {} hatte ich meinen Code, um Resultset, PreparedStatement und Connection wie gewohnt zu schließen . Sobald ich jedoch eine bestimmte Anzahl von Schleifen (1000) erreicht habe, wurde der Oracle-Fehler über zu viele offene Cursor angezeigt.

Basierend auf dem obigen Beitrag von Andrew Alcock habe ich Änderungen vorgenommen, sodass ich innerhalb der Schleife jede Ergebnismenge und jede Anweisung nach dem Abrufen der Daten und vor dem erneuten Schleifen geschlossen habe, wodurch das Problem gelöst wurde.

Darüber hinaus trat genau das gleiche Problem in einer anderen Schleife von Insert-Anweisungen in einer anderen Oracle-Datenbank (ORA-01000) auf, diesmal nach 300 Anweisungen. Wieder wurde es auf die gleiche Weise gelöst, sodass entweder das PreparedStatement oder das ResultSet oder beide als offene Cursor gelten, bis sie geschlossen werden.

Kinnison84
quelle
Das scheint nicht richtig zu sein. Spring dokumentiert, dass es für das Schließen von ResultSets verantwortlich ist ( docs.spring.io/spring/docs/current/spring-framework-reference/… ).
Ryan
Nur zur Verdeutlichung habe ich in diesen Beispielen Spring nicht verwendet.
Kinnison84
1

Haben Sie autocommit = true gesetzt? Wenn nicht, versuchen Sie Folgendes:

{ //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 
paweloque
quelle
Könnten Sie bitte auch die anderen Fragen beantworten?
Kanagavelu Sugumar
2
Autocommit schließt keine Verbindungen - es schreibt nur jede Anweisung unmittelbar nach der Ausführung automatisch fest. Wenn Sie Autocommit verwenden, erhalten Sie keinen Wert aus einer der wichtigsten Eigenschaften der Datenbank - Transaktionen. Sie können stattdessen eine NoSQL-Datenbank verwenden.
Andrew Alcock
1

Abfrage, um SQL zu finden, das geöffnet wurde.

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
Hlex
quelle
1

Dieses Problem tritt hauptsächlich auf, wenn Sie das Verbindungspooling verwenden, da beim Schließen der Verbindung diese Verbindung zum Verbindungspool zurückkehrt und alle mit dieser Verbindung verknüpften Cursor niemals geschlossen werden, da die Verbindung zur Datenbank noch offen ist. Eine Alternative besteht darin, die Leerlaufverbindungszeit von Verbindungen im Pool zu verringern. Wenn also die Verbindung beispielsweise 10 Sekunden lang im Leerlauf ist, wird die Verbindung zur Datenbank geschlossen und eine neue Verbindung zum Einfügen in den Pool erstellt.

Raveesh Badoni
quelle
0

In unserem Fall haben wir den Ruhezustand verwendet und es gab viele Variablen, die auf dieselbe zugeordnete Entität im Ruhezustand verweisen. Wir haben diese Referenzen in einer Schleife erstellt und gespeichert. Jede Referenz öffnete einen Cursor und hielt ihn offen.

Wir haben dies festgestellt, indem wir mithilfe einer Abfrage die Anzahl der geöffneten Cursor überprüft haben, während wir unseren Code ausgeführt haben, mit einem Debugger durchgegangen sind und selektiv Kommentare abgegeben haben.

Warum jede neue Referenz einen anderen Cursor öffnete - der fraglichen Entität waren Sammlungen anderer Entitäten zugeordnet, und ich denke, dies hatte etwas damit zu tun (vielleicht nicht nur dies allein, sondern in Kombination mit der Konfiguration des Abrufmodus und Cache-Einstellungen). Im Ruhezustand selbst gab es Fehler beim Öffnen geschlossener Cursor, obwohl diese in späteren Versionen behoben wurden.

Da wir sowieso nicht wirklich so viele doppelte Verweise auf dieselbe Entität haben mussten, bestand die Lösung darin, nicht mehr all diese redundanten Verweise zu erstellen und zu behalten. Einmal haben wir das Problem gemacht, wenn wir weg waren.

dshiga
quelle
0

Ich hatte dieses Problem mit meiner Datenquelle in WildFly und Tomcat, die eine Verbindung zu einem Oracle 10g herstellte.

Ich stellte fest, dass die Anweisung unter bestimmten Bedingungen nicht geschlossen wurde, selbst wenn die Anweisung.close () aufgerufen wurde. Das Problem war mit dem von uns verwendeten Oracle-Treiber: ojdbc7.jar. Dieser Treiber ist für Oracle 12c und 11g vorgesehen und es scheint einige Probleme zu geben, wenn er mit Oracle 10g verwendet wird. Daher habe ich ein Downgrade auf ojdbc5.jar durchgeführt und jetzt läuft alles einwandfrei.

Gilbertoag
quelle
0

Ich hatte das gleiche Problem, weil ich db für mehr als 1000 Iterationen abgefragt habe. Ich habe try und schließlich in meinem Code verwendet. Wurde aber immer noch fehlerhaft.

Um dies zu lösen, habe ich mich gerade bei oracle db angemeldet und die folgende Abfrage ausgeführt:

ALTER SYSTEM SET open_cursors = 8000 SCOPE = BOTH;

Und das hat mein Problem sofort gelöst.

RArora
quelle
Dies löste einige Symptome, löste das Problem jedoch nicht. Sie müssen Ihren Code korrigieren, damit er die Cursor schließt, sobald er mit ihnen fertig ist.
APC