Otrzymuję wyjątek ORA-01000 SQL. Mam więc pytania z tym związane.
- 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ń)
- Czy istnieje sposób skonfigurowania liczby obiektów instrukcji / zestawów wyników w bazie danych (np. Połączeń)?
- Czy zaleca się używanie instancji instrukcji zmiennej / obiektu wyników zamiast metody lokalnej instrukcji / obiektu wyników w środowisku jednowątkowym?
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
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
for (String language : additionalLangs) {
SYS.V$OPEN_CURSOR
widok. W ten sposób otrzymasz nie tylko identyfikator SID, ale także tekst SQL.Odpowiedzi:
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:
Błąd konfiguracji
Rozwiązanie:
Wyciek kursora
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ą:
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:
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:
Zwróć uwagę, jak klauzula last ignoruje każdy wyjątek wywołany przez funkcję close ():
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:
Jest jednak jeden wyjątek: jeśli używasz EJB lub kontenera Servlet / JSP, musisz przestrzegać ścisłego modelu wątków:
Eliminacja wycieków
Dostępnych jest wiele procesów i narzędzi ułatwiających wykrywanie i eliminowanie wycieków JDBC:
Podczas programowania - wczesne wychwytywanie błędów jest zdecydowanie najlepszym podejściem:
Praktyki programistyczne: Dobre praktyki programistyczne powinny zmniejszyć liczbę błędów w oprogramowaniu, zanim opuści biurko programisty. Konkretne praktyki obejmują:
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
W czasie wykonywania:
Zachowalność i zobowiązanie
Rejestrowanie w czasie wykonywania.
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
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.
źródło
Dodam jeszcze kilka zrozumienia.
Loggin jako sysdba.
W Putty (logowanie Oracle):
W SqlPlus:
Nazwa Użytkownika:
sys as sysdba
Ustaw wartość session_cached_cursors na 0, aby nie miała zamkniętych kursorów.
Wybierz istniejący zestaw wartości OPEN_CURSORS dla połączenia w DB
Poniżej znajduje się zapytanie o listę SID / połączeń z otwartymi wartościami kursora.
Użyj poniższego zapytania, aby zidentyfikować sql w otwartych kursorach
Teraz debuguj kod i ciesz się !!! :)
źródło
Popraw swój kod w ten sposób:
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);
źródło
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ń.
źródło
Ja też miałem do czynienia z tym problemem. Kiedyś pojawił się poniższy wyjątek
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.
źródło
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.
źródło
Czy ustawiłeś autocommit = true? Jeśli nie, spróbuj tego:
źródło
zapytanie, aby znaleźć otwarty plik sql.
źródło
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.
źródło
Korzystanie z przetwarzania wsadowego spowoduje mniejsze obciążenie. Przykłady można znaleźć pod następującym łączem: http://www.tutorialspoint.com/jdbc/jdbc-batch-processing.htm
źródło
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.
źródło
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.
źródło
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.
źródło