java.sql.SQLException: - ORA-01000: przekroczono maksymalną liczbę otwartych kursorów

115

Otrzymuję wyjątek ORA-01000 SQL. Mam więc pytania z tym związane.

  1. Czy maksymalna liczba otwartych kursorów jest dokładnie związana z liczbą połączeń JDBC, czy też są one związane z instrukcją i obiektami zestawu wyników, które utworzyliśmy dla pojedynczego połączenia? (Korzystamy z puli połączeń)
  2. Czy istnieje sposób skonfigurowania liczby obiektów instrukcji / zestawów wyników w bazie danych (np. Połączeń)?
  3. Czy zaleca się używanie instancji instrukcji zmiennej / obiektu wyników zamiast metody lokalnej instrukcji / obiektu wyników w środowisku jednowątkowym?
  4. Czy wykonanie przygotowanej instrukcji w pętli powoduje ten problem? (Oczywiście mogłem użyć sqlBatch) Uwaga: pStmt jest zamykany po zakończeniu pętli.

    { //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. Co się stanie, jeśli conn.createStatement () i conn.prepareStatement (sql) są wywoływani wiele razy w jednym obiekcie połączenia?

Edycja1: 6. Czy użycie obiektu instrukcji odniesienia Weak / Soft pomoże w zapobieganiu wyciekowi?

Edit2: 1. Czy jest jakiś sposób, żebym mógł znaleźć wszystkie brakujące instrukcje "statement.close ()" w moim projekcie? Rozumiem, że to nie jest wyciek pamięci. Ale muszę znaleźć odwołanie do instrukcji (gdzie funkcja close () nie jest wykonywana) kwalifikujące się do wyrzucania elementów bezużytecznych? Jakieś dostępne narzędzie? Czy muszę to analizować ręcznie?

Proszę, pomóż mi to zrozumieć.

Rozwiązanie

Aby znaleźć otwarty kursor w Oracle DB dla nazwy użytkownika -VELU

Przejdź do maszyny ORACLE i uruchom sqlplus jako sysdba.

[oracle@db01 ~]$ sqlplus / as sysdba 

Następnie uruchomić

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

Jeśli to możliwe, przeczytaj moją odpowiedź, aby lepiej zrozumieć moje rozwiązanie

Kanagavelu Sugumar
źródło
Czy możesz wysłać cały kod? Byłoby interesujące zobaczyć, gdzie zamykasz szelki otwierające otwarte dlafor (String language : additionalLangs) {
Jåcob
@ Kanagavelu Sugumar: dlaczego nie zadać 5 różnych pytań w SO?
Jayan,
1
Oto odpowiedź, która okazała się bardzo przydatna: stackoverflow.com/a/4507507/501113
chaotic3quilibrium
Sprawdź, czy odpowiedź jest przydatna: stackoverflow.com/questions/34716456/…
Manu
Aby śledzić otwarte kursory w Oracle, możesz również rzucić okiem na SYS.V$OPEN_CURSORwidok. W ten sposób otrzymasz nie tylko identyfikator SID, ale także tekst SQL.
Bass

Odpowiedzi:

291

ORA-01000, błąd maksymalnej liczby otwartych kursorów, jest niezwykle częstym błędem w rozwoju bazy danych Oracle. W kontekście języka Java dzieje się tak, gdy aplikacja próbuje otworzyć więcej zestawów wyników niż skonfigurowanych kursorów w instancji bazy danych.

Typowe przyczyny to:

  1. Błąd konfiguracji

    • W aplikacji masz więcej wątków wykonujących zapytania do bazy danych niż kursorów w bazie danych. W jednym przypadku masz połączenie i pulę wątków większą niż liczba kursorów w bazie danych.
    • Masz wielu programistów lub aplikacji podłączonych do tej samej instancji bazy danych (która prawdopodobnie będzie zawierać wiele schematów) i razem używasz zbyt wielu połączeń.
    • Rozwiązanie:

  2. Wyciek kursora

    • Aplikacje nie zamykają zestawów wyników (w JDBC) ani kursorów (w procedurach składowanych w bazie danych)
    • Rozwiązanie : Wycieki kursora to błędy; zwiększenie liczby kursorów w bazie danych po prostu opóźnia nieuniknioną awarię. Wycieki można znaleźć za pomocą statycznej analizy kodu , JDBC lub rejestrowania na poziomie aplikacji oraz monitorowania bazy danych .

tło

W tej sekcji opisano niektóre teorie stojące za kursorami i sposób korzystania z JDBC. Jeśli nie musisz znać tła, możesz to pominąć i przejść od razu do „Eliminacji wycieków”.

Co to jest kursor?

Kursor to zasób w bazie danych, który przechowuje stan zapytania, w szczególności pozycję, w której czytnik znajduje się w zestawie wyników. Każda instrukcja SELECT ma kursor, a procedury składowane PL / SQL mogą otwierać i używać dowolnej liczby kursorów. Możesz dowiedzieć się więcej o kursorach na Orafaq .

Instancja bazy danych zazwyczaj obsługuje kilka różnych schematów , wielu różnych użytkowników, z których każdy ma wiele sesji . Aby to zrobić, ma stałą liczbę kursorów dostępnych dla wszystkich schematów, użytkowników i sesji. Gdy wszystkie kursory są otwarte (w użyciu) i przychodzi żądanie wymagające nowego kursora, żądanie kończy się niepowodzeniem z błędem ORA-010000.

Znajdowanie i ustawianie liczby kursorów

Numer jest zwykle konfigurowany przez administratora podczas instalacji. Liczbę aktualnie używanych kursorów, maksymalną liczbę i konfigurację można uzyskać w funkcjach administratora w Oracle SQL Developer . Z SQL można to ustawić za pomocą:

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

Powiązanie JDBC w JVM z kursorami w bazie danych

Poniższe obiekty JDBC są ściśle powiązane z następującymi koncepcjami bazy danych:

  • Połączenie JDBC jest reprezentacją klienta sesji bazy danych i zapewnia transakcje bazy danych . Połączenie może mieć otwartą tylko jedną transakcję w danym momencie (ale transakcje mogą być zagnieżdżane)
  • JDBC wynikowa jest obsługiwany przez jedną kursora w bazie danych. Gdy metoda close () jest wywoływana w zestawie wyników, kursor jest zwalniany.
  • JDBC CallableStatement wywołuje procedurę składowaną w bazie danych, często napisaną w języku PL / SQL. Procedura składowana może utworzyć zero lub więcej kursorów i może zwrócić kursor jako zestaw wyników JDBC.

JDBC jest bezpieczny dla wątków: przekazywanie różnych obiektów JDBC między wątkami jest całkiem w porządku.

Na przykład możesz utworzyć połączenie w jednym wątku; inny wątek może użyć tego połączenia do utworzenia PreparedStatement, a trzeci wątek może przetworzyć zestaw wyników. Jedynym głównym ograniczeniem jest to, że w dowolnym momencie nie można mieć otwartych więcej niż jednego zestawu wyników na jednym przygotowanym zestawieniu. Zobacz Czy baza danych Oracle obsługuje wiele (równoległych) operacji na połączenie?

Zauważ, że zatwierdzenie bazy danych występuje w połączeniu, a więc wszystkie DML (INSERT, UPDATE i DELETE) w tym połączeniu zostaną zatwierdzone razem. Dlatego jeśli chcesz obsługiwać wiele transakcji w tym samym czasie, musisz mieć co najmniej jedno połączenie dla każdej współbieżnej transakcji.

Zamykanie obiektów JDBC

Typowy przykład wykonania zestawu wyników to:

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

Zwróć uwagę, jak klauzula last ignoruje każdy wyjątek wywołany przez funkcję close ():

  • Jeśli po prostu zamkniesz zestaw wyników bez try {} catch {}, może to się nie powieść i uniemożliwić zamknięcie instrukcji
  • Chcemy, aby każdy wyjątek zgłoszony w treści próby był propagowany do wywołującego. Jeśli masz pętlę, na przykład podczas tworzenia i wykonywania instrukcji, pamiętaj, aby zamknąć każdą instrukcję w pętli.

W Javie 7 firma Oracle wprowadziła interfejs AutoCloseable, który zastępuje większość standardowego schematu Java 6 jakimś ładnym cukrem składniowym.

Trzymanie obiektów JDBC

Obiekty JDBC można bezpiecznie przechowywać w zmiennych lokalnych, instancji obiektu i elementach klas. Generalnie lepszą praktyką jest:

  • Użyj instancji obiektu lub członków klasy, aby przechowywać obiekty JDBC, które są wielokrotnie używane przez dłuższy czas, takie jak Connections i PreparedStatements
  • Użyj zmiennych lokalnych dla zestawów wyników, ponieważ są one uzyskiwane, zapętlane, a następnie zamykane zwykle w zakresie pojedynczej funkcji.

Jest jednak jeden wyjątek: jeśli używasz EJB lub kontenera Servlet / JSP, musisz przestrzegać ścisłego modelu wątków:

  • Tylko serwer aplikacji tworzy wątki (za pomocą których obsługuje przychodzące żądania)
  • Tylko serwer aplikacji tworzy połączenia (które uzyskujesz z puli połączeń)
  • Podczas zapisywania wartości (stanu) między połączeniami trzeba być bardzo ostrożnym. Nigdy nie przechowuj wartości we własnych pamięciach podręcznych ani statycznych elementach członkowskich - nie jest to bezpieczne w przypadku klastrów i innych dziwnych warunków, a serwer aplikacji może wyrządzić straszne rzeczy z Twoimi danymi. Zamiast tego użyj stanowych fasoli lub bazy danych.
  • W szczególności nigdy nie zatrzymuj obiektów JDBC (Connections, ResultSets, PreparedStatements itp.) Nad różnymi zdalnymi wywołaniami - pozwól, aby zarządzał tym serwer aplikacji. Serwer aplikacji nie tylko udostępnia pulę połączeń, ale także buforuje Twoje PreparedStatements.

Eliminacja wycieków

Dostępnych jest wiele procesów i narzędzi ułatwiających wykrywanie i eliminowanie wycieków JDBC:

  1. Podczas programowania - wczesne wychwytywanie błędów jest zdecydowanie najlepszym podejściem:

    1. Praktyki programistyczne: Dobre praktyki programistyczne powinny zmniejszyć liczbę błędów w oprogramowaniu, zanim opuści biurko programisty. Konkretne praktyki obejmują:

      1. Programowanie w parach, aby kształcić osoby bez wystarczającego doświadczenia
      2. Recenzje kodu, ponieważ wiele oczu jest lepszych niż jedno
      3. Testowanie jednostkowe, co oznacza, że ​​możesz przetestować całą bazę kodu za pomocą narzędzia testowego, co sprawia, że ​​odtwarzanie wycieków jest banalne
      4. Użyj istniejących bibliotek do tworzenia puli połączeń, zamiast tworzyć własne
    2. Statyczna analiza kodu: użyj narzędzia takiego jak doskonałe Findbugs, aby przeprowadzić statyczną analizę kodu. Wybiera to wiele miejsc, w których metoda close () nie została poprawnie obsłużona. Findbugs ma wtyczkę do Eclipse, ale działa również samodzielnie w przypadku jednorazowych czynności, ma integrację z Jenkins CI i innymi narzędziami do kompilacji

  2. W czasie wykonywania:

    1. Zachowalność i zobowiązanie

      1. Jeśli możliwość przechowywania zestawu wyników to ResultSet.CLOSE_CURSORS_OVER_COMMIT, zestaw wyników jest zamykany po wywołaniu metody Connection.commit (). Można to ustawić za pomocą Connection.setHoldability () lub przeciążonej metody Connection.createStatement ().
    2. Rejestrowanie w czasie wykonywania.

      1. Umieść dobre instrukcje dziennika w swoim kodzie. Powinny one być jasne i zrozumiałe, aby klient, personel pomocniczy i członkowie zespołu mogli zrozumieć bez szkolenia. Powinny być zwięzłe i obejmować drukowanie stanu / wartości wewnętrznych kluczowych zmiennych i atrybutów, tak aby można było śledzić logikę przetwarzania. Dobre rejestrowanie jest podstawą debugowania aplikacji, zwłaszcza tych, które zostały wdrożone.
      2. Możesz dodać debugujący sterownik JDBC do swojego projektu (do debugowania - nie wdrażaj go). Jednym z przykładów (nie używałem go) jest log4jdbc . Następnie musisz wykonać prostą analizę tego pliku, aby zobaczyć, które pliki wykonywalne nie mają odpowiedniego zamknięcia. Liczenie otwarcia i zamknięcia powinno pokazać, czy istnieje potencjalny problem

        1. Monitorowanie bazy danych. Monitoruj uruchomioną aplikację za pomocą narzędzi, takich jak funkcja „Monitor SQL” programisty SQL lub TOAD firmy Quest . Monitorowanie opisano w tym artykule . Podczas monitorowania odpytujesz otwarte kursory (np. Z tabeli v $ sesstat) i przeglądasz ich SQL. Jeśli liczba kursorów rośnie i (co najważniejsze) jest zdominowana przez jedną identyczną instrukcję SQL, wiesz, że masz przeciek z tym kodem SQL. Przeszukaj swój kod i przejrzyj.

Inne przemyślenia

Czy możesz używać WeakReferences do obsługi zamykania połączeń?

Słabe i miękkie referencje to sposoby na umożliwienie odniesienia do obiektu w sposób, który pozwala JVM zebrać odwołanie do pamięci w dowolnym momencie, który uzna za stosowny (zakładając, że nie ma silnych łańcuchów odwołań do tego obiektu).

Jeśli przekażesz ReferenceQueue w konstruktorze do miękkiego lub słabego Reference, obiekt zostanie umieszczony w ReferenceQueue, gdy obiekt zostanie poddany GC, gdy wystąpi (jeśli w ogóle wystąpi). Dzięki takiemu podejściu możesz wchodzić w interakcje z finalizacją obiektu iw tym momencie możesz zamknąć lub sfinalizować obiekt.

Odniesienia do fantomów są nieco dziwniejsze; ich celem jest tylko kontrolowanie finalizacji, ale nigdy nie można uzyskać odniesienia do oryginalnego obiektu, więc trudno będzie wywołać na nim metodę close ().

Jednak rzadko dobrym pomysłem jest próba kontrolowania, kiedy GC jest uruchamiany (Weak, Soft i PhantomReferences informują o tym, że obiekt jest umieszczony w kolejce do GC). W rzeczywistości, jeśli ilość pamięci w JVM jest duża (np. -Xmx2000m), możesz nigdy nie wykonać GC obiektu i nadal będziesz korzystać z ORA-01000. Jeśli pamięć maszyny JVM jest mała w stosunku do wymagań programu, może się okazać, że obiekty ResultSet i PreparedStatement są poddawane GC natychmiast po utworzeniu (zanim będzie można z nich odczytać), co prawdopodobnie zakończy się niepowodzeniem.

TL; DR: słaby mechanizm odniesienia nie jest dobrym sposobem na zarządzanie i zamykanie obiektów Statement i ResultSet.

Andrew Alcock
źródło
3
Jeśli tworzysz instrukcje w pętli, upewnij się, że jest ona zamknięta w pętli, w przeciwnym razie zamkniesz tylko ostatnią instrukcję.
basiljames,
Dzięki, Basiljames. Właśnie zredagowałem odpowiedź, aby dodać podany przez Ciebie punkt.
Andrew Alcock,
@ Andrew Alcock Wielkie dzięki! Andrzej. Czy mógłbyś odpowiedzieć również na szóstą.
Kanagavelu Sugumar,
@AndrewAlcock Proszę… proszę… proszę… odpowiedz również na moje 7 pytanie. Od czasu naszego projektu bardzo często spotykamy się z ORA-01000 podczas testów obciążeniowych. Twoje wkłady są dla mnie cenniejsze. Dzięki za tonę z góry !!
Kanagavelu Sugumar
RE: 7 - możesz spróbować wyszukiwania bliskości za pomocą narzędzia takiego jak grep. Kiedy rozpoznasz SQL (wybierz, wstaw, zaktualizuj, usuń), sprawdź bliskość słowa close () obok instrukcji. Jeśli bliskość jest dalej niż oczekiwano, może to być sposób na zbadanie, gdzie jej brakuje. lightboxtechnologies.com/2012/07/27/…
niedz.
28

Dodam jeszcze kilka zrozumienia.

  1. Kursor dotyczy tylko przedmiotu stwierdzenia; Nie jest to ani resultSet, ani obiekt połączenia.
  2. Ale nadal musimy zamknąć zestaw wyników, aby uwolnić część pamięci wyroczni. Mimo to, jeśli nie zamkniesz zestawu wyników, który nie będzie liczony dla CURSORS.
  3. Obiekt Closing Statement automatycznie zamyka również obiekt wyników.
  4. Kursor zostanie utworzony dla wszystkich instrukcji SELECT / INSERT / UPDATE / DELETE.
  5. Każdą instancję ORACLE DB można zidentyfikować za pomocą identyfikatora SID oracle; podobnie ORACLE DB może zidentyfikować każde połączenie za pomocą identyfikatora SID połączenia. Oba identyfikatory SID są różne.
  6. Więc sesja ORACLE to nic innego jak połączenie jdbc (tcp); który jest niczym innym jak jednym identyfikatorem SID.
  7. Jeśli ustawimy maksymalną liczbę kursorów na 500, będzie to dotyczyło tylko jednej sesji JDBC / połączenia / SID.
  8. Możemy więc mieć wiele połączeń JDBC z odpowiednią liczbą kursorów (instrukcji).
  9. Po zakończeniu pracy maszyny JVM wszystkie połączenia / kursory zostaną zamknięte LUB JDBCConnection zostanie zamknięte. KURSORY w odniesieniu do tego połączenia zostaną zamknięte.

Loggin jako sysdba.

W Putty (logowanie Oracle):

  [oracle@db01 ~]$ sqlplus / as sysdba

W SqlPlus:

Nazwa Użytkownika: sys as sysdba

Ustaw wartość session_cached_cursors na 0, aby nie miała zamkniętych kursorów.

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

Wybierz istniejący zestaw wartości OPEN_CURSORS dla połączenia w DB

 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;

Poniżej znajduje się zapytanie o listę SID / połączeń z otwartymi wartościami kursora.

 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'

Użyj poniższego zapytania, aby zidentyfikować sql w otwartych kursorach

 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'

Teraz debuguj kod i ciesz się !!! :)

Kanagavelu Sugumar
źródło
1
Oto kolejne zapytanie, które wydaje się działać dobrze: stackoverflow.com/a/2560415/32453
rogerdpack
4

Popraw swój kod w ten sposób:

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

Czy jesteś pewien, że naprawdę zamykasz swoje pStatements, połączenia i wyniki?

Aby przeanalizować otwarte obiekty, można zaimplementować wzorzec delegatora, który otacza kod wokół obiektów stanu, połączeń i wyników. Więc zobaczysz, czy obiekt zostanie pomyślnie zamknięty.

Przykład dla: 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
źródło
3

Jeśli aplikacja jest aplikacją Java EE działającą na serwerze aplikacji Oracle WebLogic, możliwą przyczyną tego problemu jest rozmiar pamięci podręcznej instrukcji ustawienie w programie WebLogic.

Jeśli ustawienie Rozmiar pamięci podręcznej instrukcji dla określonego źródła danych jest w przybliżeniu równe lub większe od ustawienia maksymalnej liczby otwartych kursorów w bazie danych Oracle, wówczas wszystkie otwarte kursory mogą być używane przez buforowane instrukcje SQL, które są utrzymywane jako otwarte przez WebLogic, w błędzie ORA-01000.

Aby rozwiązać ten problem, zmniejsz ustawienie rozmiaru pamięci podręcznej instrukcji dla każdego źródła danych WebLogic, które wskazuje, że baza danych Oracle jest znacznie mniejsze niż ustawienie maksymalnej liczby kursorów w bazie danych.

W konsoli administracyjnej WebLogic 10 ustawienie Rozmiar pamięci podręcznej instrukcji dla każdego źródła danych można znaleźć w sekcji Usługi (nawigacja po lewej stronie)> Źródła danych> (indywidualne źródło danych)> karta Pula połączeń.

Jon Schneider
źródło
1
Hibernate ma również pamięć podręczną instrukcji. Zobacz także developer.jboss.org/wiki/…
Pino
3

Ja też miałem do czynienia z tym problemem. Kiedyś pojawił się poniższy wyjątek

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

Używałem Spring Framework z Spring JDBC dla warstwy dao.

Moja aplikacja w jakiś sposób przeciekała kursory i po kilku minutach dawała mi ten wyjątek.

Po wielu dokładnych debugowaniu i analizie stwierdziłem, że wystąpił problem z indeksowaniem, kluczem podstawowym i unikalnymi ograniczeniami w jednej z tabel używanych w wykonywanym zapytaniu .

Moja aplikacja próbowała zaktualizować kolumny, które zostały omyłkowo zindeksowane . Tak więc za każdym razem, gdy moja aplikacja trafiała na zapytanie o aktualizację w indeksowanych kolumnach, baza danych próbowała przeprowadzić ponowne indeksowanie w oparciu o zaktualizowane wartości. To przeciekało kursory .

Udało mi się rozwiązać problem, wykonując Właściwe indeksowanie na kolumnach, które były używane do wyszukiwania w zapytaniu i stosując odpowiednie ograniczenia wszędzie tam, gdzie było to wymagane.

Piyush Verma
źródło
2

Dzisiaj stanąłem przed tym samym problemem (ORA-01000). Miałem pętlę for w try {}, aby wielokrotnie wykonywać instrukcję SELECT w bazie danych Oracle (za każdym razem, gdy zmieniał parametr), a na końcu {} miałem swój kod, aby zamknąć zestaw wyników, przygotowanie i połączenie jak zwykle . Ale gdy tylko osiągnąłem określoną liczbę pętli (1000), otrzymałem błąd Oracle dotyczący zbyt wielu otwartych kursorów.

Opierając się na powyższym poście Andrew Alcocka, wprowadziłem zmiany tak, że wewnątrz pętli zamknąłem każdy zestaw wyników i każdą instrukcję po uzyskaniu danych i przed ponownym zapętleniem, co rozwiązało problem.

Dodatkowo dokładnie ten sam problem wystąpił w innej pętli instrukcji Insert, w innej bazie danych Oracle (ORA-01000), tym razem po 300 instrukcjach. Ponownie zostało to rozwiązane w ten sam sposób, więc albo PreparedStatement, albo ResultSet, albo oba, liczą się jako otwarte kursory, dopóki nie zostaną zamknięte.

Kinnison84
źródło
To nie wydaje się właściwe. Spring dokumentuje, że jest odpowiedzialny za zamykanie ResultSets ( docs.spring.io/spring/docs/current/spring-framework-reference/ ... ).
Ryan
tylko dla wyjaśnienia, w tych przykładach nie używałem Springa.
Kinnison84
1

Czy ustawiłeś autocommit = true? Jeśli nie, spróbuj tego:

{ //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 
pawelok
źródło
Czy mógłbyś odpowiedzieć na inne pytania?
Kanagavelu Sugumar
2
Autocommit nie zamyka połączeń - tylko automatycznie zatwierdza każdą instrukcję natychmiast po wykonaniu. Jeśli używasz autocommit, nie uzyskujesz wartości z jednej z najważniejszych właściwości bazy danych - transakcji. Zamiast tego możesz rozważyć użycie bazy danych NoSQL.
Andrew Alcock,
1

zapytanie, aby znaleźć otwarty plik 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
Hlex
źródło
1

Ten problem występuje głównie podczas korzystania z puli połączeń, ponieważ po zamknięciu połączenia to połączenie wraca do puli połączeń, a wszystkie kursory skojarzone z tym połączeniem nigdy nie są zamykane, ponieważ połączenie z bazą danych jest nadal otwarte. Tak więc jedną z alternatyw jest skrócenie czasu bezczynności połączeń w puli, więc może za każdym razem, gdy połączenie pozostanie bezczynne w połączeniu przez powiedzmy 10 sekund, połączenie z bazą danych zostanie zamknięte i zostanie utworzone nowe połączenie do umieszczenia w puli.

Raveesh Badoni
źródło
0

W naszym przypadku używaliśmy Hibernate i mieliśmy wiele zmiennych odnoszących się do tej samej jednostki mapowanej w Hibernate. Tworzyliśmy i zapisywaliśmy te odniesienia w pętli. Każde odniesienie otwierało kursor i utrzymywało go otwartym.

Odkryliśmy to, używając zapytania do sprawdzania liczby otwartych kursorów podczas uruchamiania naszego kodu, przechodzenia przez debuger i selektywnego komentowania rzeczy.

Co do tego, dlaczego każde nowe odniesienie otwierało kolejny kursor - przedmiot, o którym mowa, miał przypisane do niego kolekcje innych podmiotów i myślę, że miało to coś wspólnego (być może nie tylko samo, ale w połączeniu z tym, jak skonfigurowaliśmy tryb pobierania i ustawienia pamięci podręcznej). Sam Hibernate zawierał błędy związane z niepowodzeniem zamykania otwartych kursorów, chociaż wygląda na to, że zostały one naprawione w późniejszych wersjach.

Ponieważ i tak nie musieliśmy mieć tylu zduplikowanych odniesień do tej samej encji, rozwiązaniem było zaprzestanie tworzenia i utrzymywania tych wszystkich zbędnych odniesień. Kiedyś zrobiliśmy to problem, gdy nie było.

dshiga
źródło
0

Miałem ten problem z moim źródłem danych w WildFly i Tomcat, łącząc się z Oracle 10g.

Zauważyłem, że pod pewnymi warunkami instrukcja nie została zamknięta, nawet gdy wywołano instrukcję.close (). Problem dotyczył sterownika Oracle, którego używaliśmy: ojdbc7.jar. Ten sterownik jest przeznaczony dla Oracle 12c i 11g i wydaje się, że ma pewne problemy, gdy jest używany z Oracle 10g, więc obniżam wersję do ojdbc5.jar i teraz wszystko działa poprawnie.

gilbertoag
źródło
0

Napotkałem ten sam problem, ponieważ odpytywałem bazę danych dla ponad 1000 iteracji. Użyłem try i wreszcie w moim kodzie. Ale nadal pojawiał się błąd.

Aby rozwiązać ten problem, właśnie zalogowałem się do bazy danych Oracle i uruchomiłem poniższe zapytanie:

ALTER SYSTEM SET open_cursors = 8000 ZAKRES = BOTH;

I to natychmiast rozwiązało mój problem.

RArora
źródło
To złagodziło niektóre objawy, ale tak naprawdę nie rozwiązało problemu. Musisz naprawić kod, aby zamykał kursory po zakończeniu pracy.
APC