Ich erhalte eine ORA-01000-SQL-Ausnahme. Ich habe also einige Fragen dazu.
Führt die Ausführung einer vorbereiteten Anweisung in einer Schleife zu diesem Problem? (Natürlich hätte ich 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
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", um das Auslaufen zu verhindern?
Edit2: 1. Gibt es eine Möglichkeit, alle fehlenden "statement.close ()" in meinem Projekt zu finden? Ich verstehe, dass es kein Speicherleck ist. Aber ich muss eine Anweisungsreferenz finden (wo close () nicht ausgeführt wird), die für die Müllsammlung geeignet ist. Irgendein Werkzeug verfügbar? Oder muss ich es manuell analysieren?
Bitte hilf mir, es zu verstehen.
Gehen Sie zur ORALCE-Maschine und starten Sie sqlplus als sysdba.
[[email protected] ~]$ 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';
Bitte lesen Sie meine Antwort am Ende.
ORA-01000, der Fehler "Maximum-Open-Cursors", ist ein äußerst häufiger Fehler in der Oracle-Datenbankentwicklung. Im Kontext von Java geschieht dies, wenn die Anwendung versucht, mehr ResultSets zu öffnen, als konfigurierte Cursor auf einer Datenbankinstanz vorhanden sind.
Häufige Ursachen sind:
Konfigurationsfehler
Lösung:
Cursor undicht
In diesem Abschnitt werden einige der Cursortheorien und die Verwendung von JDBC beschrieben. Wenn Sie den Hintergrund nicht kennen müssen, können Sie diesen überspringen und direkt zu "Lecks beseitigen" wechseln.
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 hat einen Cursor, und gespeicherte PL/SQL-Prozeduren können so viele Cursor öffnen und verwenden, wie sie benötigen. Weitere Informationen zu Cursorn finden Sie unter Orafaq .
Eine Datenbankinstanz bedient normalerweise mehrere verschiedene Schemata und 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.
Die Nummer wird normalerweise vom DBA bei der Installation konfiguriert. Auf die Anzahl der aktuell verwendeten Cursor, die maximale Anzahl und die Konfiguration kann in den Administratorfunktionen in Oracle SQL Developer zugegriffen werden. In SQL kann eingestellt werden mit:
ALTER SYSTEM SET OPEN_CURSORS=1337 SID='*' SCOPE=BOTH;
Die folgenden JDBC-Objekte sind eng an die folgenden Datenbankkonzepte gekoppelt:
JDBC ist threadsicher: Es ist 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 auf einem einzelnen PreparedStatement nicht mehr als ein ResultSet geöffnet sein darf. Siehe nterstützt Oracle DB mehrere (parallele) Operationen pro Verbindung?
Beachten Sie, dass ein Datenbank-Commit für eine Verbindung erfolgt und daher alle DML-Dateien (INSERT, UPDATE und DELETE) für diese Verbindung zusammen festgeschrieben werden. Wenn Sie also mehrere Transaktionen gleichzeitig unterstützen möchten, müssen Sie für jede gleichzeitige Transaktion mindestens eine Verbindung haben.
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 alle durch close () ausgelösten Ausnahmen ignoriert:
In Java 7 hat Oracle die AutoCloseable-Schnittstelle eingeführt, die den größten Teil des Java 6-Boilerplates durch etwas syntaktischen Zucker aus Nizza ersetzt.
JDBC-Objekte können sicher in lokalen Variablen, Objektinstanzen und Klassenmitgliedern gespeichert werden. Es ist im Allgemeinen besser, Folgendes zu tun:
Es gibt jedoch eine Ausnahme: Wenn Sie EJBs oder einen Servlet/JSP-Container verwenden, müssen Sie ein striktes Threading-Modell befolgen:
Es stehen eine Reihe von Prozessen und Tools zur Verfügung, mit denen JDBC-Lecks erkannt und beseitigt werden können:
Während der Entwicklung ist es bei weitem am besten, Fehler frühzeitig zu erkennen:
Entwicklungspraktiken: Gute Entwicklungspraktiken sollten die Anzahl der Fehler in Ihrer Software verringern, bevor diese den Entwickler-Schreibtisch verlässt. Spezifische Praktiken umfassen:
Statische Code-Analyse: Verwenden Sie ein Tool wie das ausgezeichnete Findbugs , um eine statische Code-Analyse durchzuführen. Dies greift viele Stellen auf, an denen close () nicht korrekt behandelt wurde. Findbugs hat ein Plugin für Eclipse, läuft aber auch als Einzelplatzversion und ist in Jenkins CI und andere Build-Tools integriert
Zur Laufzeit:
Haltbarkeit und Commit
Protokollierung zur Laufzeit.
Sie können Ihrem Projekt einen Debugging-JDBC-Treiber hinzufügen (zum Debuggen - stellen Sie ihn nicht tatsächlich bereit). Ein Beispiel (ich habe es nicht verwendet) ist log4jdbc . Sie müssen dann einige einfache Analysen für diese Datei durchführen, um festzustellen, welche Ausführungen keinen entsprechenden Abschluss haben. Das Zählen von Öffnen und Schließen sollte aufzeigen, ob ein potenzielles Problem vorliegt
Mit schwachen und weichen Referenzen können Sie ein Objekt auf eine Weise referenzieren, die es der JVM ermöglicht, den Referenten zu jedem Zeitpunkt, den sie für geeignet hält, mit Garbage zu sammeln (vorausgesetzt, es gibt keine starken Referenzketten zu diesem Objekt).
Wenn Sie eine ReferenceQueue im Konstruktor an die weiche oder schwache Referenz übergeben, wird das Objekt in die ReferenceQueue eingefügt, wenn das Objekt zum Zeitpunkt seines Auftretens GC-geprüft wird (falls überhaupt). 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 Finalisierung zu steuern. Sie können jedoch niemals einen Verweis auf das ursprüngliche Objekt erhalten. Daher wird es schwierig sein, die Methode close () für dieses Objekt aufzurufen.
Es ist jedoch selten eine gute Idee, zu versuchen, zu steuern, wann der GC ausgeführt wird (Weak, Soft und PhantomReferences lassen Sie wissen , nachdem , dass sich das Objekt in der Warteschlange für den GC befindet). Wenn die Speicherkapazität in der JVM groß ist (z. B. -Xmx2000m), können Sie das Objekt möglicherweise nie GC, und der ORA-01000 tritt weiterhin auf. Wenn der JVM-Speicher im Verhältnis zu den Anforderungen Ihres Programms klein ist, stellen Sie möglicherweise fest, dass die Objekte ResultSet und PreparedStatement unmittelbar nach der Erstellung (bevor Sie daraus lesen können) gecodet werden, was wahrscheinlich zu einem Fehlschlagen Ihres Programms führt.
TL; DR: Der schwache Referenzmechanismus ist keine gute Methode zum Verwalten und Schließen von Statement- und ResultSet-Objekten.
Ich füge noch ein wenig mehr Verständnis hinzu.
Melden Sie sich als sysdba an.
In PuTTY (Oracle-Login):
[[email protected] ~]$ sqlplus / as sysdba
In SqlPlus:
Benutzername: sys as sysdba
alter session set session_cached_cursors=0
select * from V$PARAMETER where name='session_cached_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;
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'
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 es !!! :)
Korrigieren Sie Ihren Code wie folgt:
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 Delegator-Muster implementieren, das Code um Statemant-, Verbindungs- und Ergebnis-Objekte bindet. Sie sehen also, ob ein Objekt erfolgreich geschlossen wird.
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");
}
}
}
Wenn es sich bei Ihrer Anwendung um eine Java EE-Anwendung handelt, die auf Oracle WebLogic als Anwendungsserver ausgeführt wird, kann dies durch die Einstellung Statement Cache Size in WebLogic verursacht werden.
Wenn die Einstellung für die Größe des Anweisungscaches für eine bestimmte Datenquelle mindestens der Einstellung für die maximale Anzahl der geöffneten Cursor in der Oracle-Datenbank entspricht oder größer ist, können alle geöffneten 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 Statement-Cache-Größe für jede WebLogic-Datenquelle, die auf die Oracle-Datenbank verweist, auf deutlich weniger als die Einstellung für die maximale Cursoranzahl in der Datenbank.
In der WebLogic 10 Admin Console finden Sie die Einstellung Statement Cache Size für jede Datenquelle unter Dienste (linke Navigation)> Datenquellen> (einzelne Datenquelle)> Registerkarte Verbindungspool.
Ich hatte heute das gleiche Problem (ORA-01000). Ich hatte eine for-Schleife in try {}, um eine SELECT-Anweisung in einer Oracle-Datenbank mehrmals auszuführen (jedes Mal, wenn ein Parameter geändert wurde), und in der letzten {} hatte ich meinen Code, um Resultset, PreparedStatement und Connection wie gewöhnlich zu schließen . Sobald ich jedoch eine bestimmte Anzahl von Schleifen (1000) erreicht hatte, bekam ich den Oracle-Fehler über zu viele offene Cursor.
Basierend auf dem Beitrag von Andrew Alcock oben habe ich Änderungen vorgenommen, so dass inside the loop, jedes Resultset und jede Anweisung nach dem Abrufen der Daten und vor dem erneuten Durchlaufen der Schleife geschlossen wurde. Dadurch wurde das Problem gelöst.
Das gleiche Problem trat außerdem in einer anderen Schleife von Insert-Anweisungen in einer anderen Oracle-Datenbank (ORA-01000) auf, diesmal nach 300 Anweisungen. Wieder wurde es auf dieselbe Weise gelöst, sodass entweder das PreparedStatement oder das ResultSet oder beide als offene Cursor gelten, bis sie geschlossen werden.
abfrage, um die geöffnete SQL zu finden.
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
Auch ich war mit diesem Problem konfrontiert. Die folgende Ausnahme kam
Java.sql.SQLException: - ORA-01000: maximum open cursors exceeded
Ich habe Spring Framework mit Spring JDBC für Dao-Layer verwendet.
Meine Anwendung hat irgendwann Cursors durchgesickert und nach ein paar Minuten gab es diese Ausnahme.
Nach gründlichem Debugging und Analyse stellte ich fest, dass es ein Problem mit dem Indexing, Primary Key und Unique Constraints in einer der Table gab, das in der Query i ausgeführt wurde .
Meine Anwendung hat versucht, die Spalten zu aktualisieren, die fälschlicherweise Indexed waren ..__ Wenn also meine Anwendung die Aktualisierungsabfrage für die indizierten Spalten traf, versuchte die Datenbank die Neuindizierung auf der Grundlage der aktualisierte Werte. Es liefen die Cursor .
Ich konnte das Problem lösen, indem ich für die Spalten, die zum Durchsuchen in der Abfrage verwendet wurden, die richtige Indexierung durchführte und gegebenenfalls geeignete Einschränkungen anwendete.
Haben Sie autocommit = true gesetzt? Wenn nicht, probiere es aus:
{ //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
In unserem Fall verwendeten wir Hibernate und hatten viele Variablen, die auf dieselbe zugeordnete Hibernate-Entität verweisen. Wir haben diese Referenzen in einer Schleife erstellt und gespeichert. Jede Referenz öffnete einen Cursor und hielt ihn offen.
Wir entdeckten dies, indem wir mithilfe einer -Query die Anzahl der geöffneten Cursor überprüften, während wir unseren Code liefen, mit einem Debugger schrittweise vorgehen und die Dinge selektiv kommentieren.
Warum jede neue Referenz einen anderen Cursor geöffnet hat - der betreffenden Entität waren Sammlungen anderer Entitäten zugeordnet, und ich denke, das hatte etwas damit zu tun (vielleicht nicht nur allein, sondern in Kombination mit der Konfiguration des Abrufmodus und Cache-Einstellungen). Hibernate selbst hatte bugs um nicht geschlossene Cursor geschlossen, obwohl es so aussieht, als ob diese in späteren Versionen behoben wurden.
Da wir ohnehin nicht so viele doppelte Verweise auf dieselbe Entität brauchten, bestand die Lösung darin, das Erstellen und Verwalten all dieser redundanten Verweise zu beenden. Einmal haben wir das Problem gemacht, wenn wir weg waren.
Ich hatte dieses Problem mit meiner Datenquelle in WildFly und Tomcat, wenn eine Verbindung zu einem Oracle 10g hergestellt wurde.
Ich habe festgestellt, dass die Anweisung unter bestimmten Bedingungen nicht geschlossen wurde, auch wenn die Anweisung statement.close () aufgerufen wurde .. Das Problem war mit dem von uns verwendeten Oracle-Treiber: ojdbc7.jar. Dieser Treiber ist für Oracle 12c und 11g gedacht, und es scheint, als ob er mit Oracle 10g verwendet wird, einige Probleme. Ich habe daher ein Downgrade auf ojdbc5.jar vorgenommen, und jetzt läuft alles einwandfrei.
Ich hatte das gleiche Problem, weil ich db für mehr als 1000 Iterationen abgefragt habe. War aber immer noch fehlerhaft.
Um dieses Problem zu lösen, habe ich mich gerade bei Oracle db angemeldet und unter der folgenden Abfrage ausgeführt:
ALTER SYSTEM SET open_cursors = 8000 SCOPE = BEIDE;
Und damit wurde mein Problem sofort gelöst.
Dieses Problem tritt hauptsächlich dann auf, wenn Sie das Verbindungspooling verwenden, da beim Schließen der Verbindung diese Verbindung wieder zum Verbindungspool zurückkehrt und der mit dieser Verbindung verbundene Cursor nie geschlossen wird, da die Verbindung zur Datenbank noch geöffnet ist .. Eine Alternative ist Verringern Sie die Leerlaufverbindungszeit von Verbindungen im Pool. Wenn also die Verbindung 10 Sekunden lang inaktiv bleibt, wird die Verbindung zur Datenbank geschlossen und eine neue Verbindung zum Einrichten des Pools erstellt.
Die Verwendung der Stapelverarbeitung führt zu weniger Aufwand. Beispiele finden Sie unter dem folgenden Link: http://www.tutorialspoint.com/jdbc/jdbc-batch-processing.htm