Oracle
 sql >> база данни >  >> RDS >> Oracle

java.sql.SQLException:- ORA-01000:максималният отворен курсор е превишен

ORA-01000, грешката при максимално отваряне на курсори, е изключително често срещана грешка в разработването на база данни на Oracle. В контекста на Java това се случва, когато приложението се опита да отвори повече ResultSets, отколкото има конфигурирани курсори в екземпляр на база данни.

Честите причини са:

  1. Грешка в конфигурацията

    • Имате повече нишки във вашето приложение, отправящи заявки към базата данни, отколкото курсори в DB. Един случай е, когато имате връзка и пул от нишки, по-голям от броя на курсорите в базата данни.
    • Имате много разработчици или приложения, свързани към един и същ DB екземпляр (което вероятно ще включва много схеми) и заедно използвате твърде много връзки.
    • Решение:

      • Увеличаване на броя на курсорите в базата данни (ако ресурсите позволяват) или
      • Намаляване на броя на нишките в приложението.
  2. Изтичане на курсор

    • Приложенията не затварят ResultSets (в JDBC) или курсори (в съхранените процедури в базата данни)
    • Решение :Течовете на курсора са бъгове; увеличаването на броя на курсорите в DB просто забавя неизбежния отказ. Течовете могат да бъдат открити с помощта на статичен кодов анализ, JDBC или регистриране на ниво приложение и мониторинг на база данни.

Фон

Този раздел описва част от теорията зад курсорите и как трябва да се използва JDBC. Ако не е нужно да знаете фона, можете да пропуснете това и да преминете направо към „Елиминиране на течове“.

Какво е курсор?

Курсорът е ресурс в базата данни, който съдържа състоянието на заявка, по-специално позицията, където четецът е в ResultSet. Всеки оператор SELECT има курсор и PL/SQL съхранените процедури могат да отварят и използват толкова курсори, колкото им е необходимо. Можете да научите повече за курсорите в Orafaq.

Екземпляр на база данни обикновено обслужва няколко различни схеми , много различни потребители всяка с много сесии . За да направите това, той има фиксиран брой курсори, достъпни за всички схеми, потребители и сесии. Когато всички курсори са отворени (използвани) и постъпи заявка, която изисква нов курсор, заявката се проваля с грешка ORA-010000.

Намиране и настройка на броя на курсорите

Номерът обикновено се конфигурира от DBA при инсталиране. Броят на използваните в момента курсори, максималният брой и конфигурацията могат да бъдат достъпни във функциите на администратора в Oracle SQL Developer. От SQL може да се настрои с:

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

Свързване на JDBC в JVM с курсорите в DB

JDBC обектите по-долу са тясно свързани със следните концепции за база данни:

  • JDBC Връзка е клиентското представяне на сесия на база данни и предоставя транзакции в базата данни . Една връзка може да има отворена само една транзакция по всяко време (но транзакциите могат да бъдат вложени)
  • JDBC ResultSet се поддържа от един курсор в базата данни. Когато close() се извика в ResultSet, курсорът се освобождава.
  • JDBC CallableStatement извиква съхранена процедура в базата данни, често написани на PL/SQL. Съхранената процедура може да създаде нула или повече курсори и може да върне курсор като JDBC ResultSet.

JDBC е безопасен за нишки:Съвсем добре е да предавате различните JDBC обекти между нишките.

Например, можете да създадете връзката в една нишка; друга нишка може да използва тази връзка за създаване на PreparedStatement и трета нишка може да обработи набора от резултати. Единственото основно ограничение е, че не можете да имате повече от един ResultSet отворен в един PreparedStatement по всяко време. Вижте Oracle DB поддържа ли множество (паралелни) операции на връзка?

Обърнете внимание, че записване на база данни се извършва при връзка и така всички DML (INSERT, UPDATE и DELETE) на тази връзка ще се записват заедно. Следователно, ако искате да поддържате няколко транзакции едновременно, трябва да имате поне една връзка за всяка едновременна транзакция.

Затваряне на 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():

  • Ако просто затворите ResultSet без try {} catch {}, той може да се провали и да попречи на затварянето на изявление
  • Искаме да позволим всяко изключение, повдигнато в тялото на опита, да се разпространи към повикващия. Ако имате цикъл върху, например създаване и изпълнение на оператори, не забравяйте да затворите всеки изявление в цикъла.

В Java 7 Oracle въведе интерфейса AutoCloseable, който заменя по-голямата част от шаблона на Java 6 с малко хубава синтактична захар.

Задържане на JDBC обекти

JDBC обектите могат безопасно да се държат в локални променливи, екземпляр на обект и членове на клас. Като цяло е по-добра практика да:

  • Използвайте екземпляр на обект или членове на клас, за да държите JDBC обекти, които се използват многократно за по-дълъг период, като Connections и PreparedStatements
  • Използвайте локални променливи за ResultSets, тъй като те се получават, преминават през цикъл и след това се затварят обикновено в рамките на обхвата на една функция.

Има обаче едно изключение:Ако използвате EJB или Servlet/JSP контейнер, трябва да следвате строг модел на нишки:

  • Само сървърът на приложения създава нишки (с които обработва входящи заявки)
  • Само сървърът на приложения създава връзки (които получавате от пула за връзки)
  • Когато запазвате стойности (състояние) между повикванията, трябва да сте много внимателни. Никога не съхранявайте стойности в собствените си кешове или статични членове - това не е безопасно в клъстери и други странни условия и сървърът на приложения може да направи ужасни неща с вашите данни. Вместо това използвайте бобове със състояние или база данни.
  • По-специално, никога задръжте JDBC обекти (Connections, ResultSets, PreparedStatements и т.н.) върху различни отдалечени извиквания - нека сървърът на приложения управлява това. Сървърът на приложенията не само предоставя пул за връзки, но и кешира вашите PreparedStatements.

Отстраняване на течове

Съществуват редица процеси и инструменти, които помагат при откриването и елиминирането на течове на JDBC:

  1. По време на разработката - ранното улавяне на грешки е най-добрият подход:

    1. Практики за разработка:Добрите практики за разработка трябва да намалят броя на грешките във вашия софтуер, преди той да напусне бюрото на разработчика. Специфичните практики включват:

      1. Програмиране по двойки, за обучение на тези без достатъчно опит
      2. Кодирайте прегледи, защото много очи са по-добри от едно
      3. Единично тестване, което означава, че можете да упражнявате всяка и цялата си кодова база от инструмент за тестване, което прави възпроизвеждането на течове тривиално
      4. Използвайте съществуващи библиотеки за обединяване на връзки, вместо да създавате свои собствени
    2. Анализ на статичен код:Използвайте инструмент като отличния Findbugs за извършване на статичен анализ на кода. Това улавя много места, където close() не е обработено правилно. Findbugs има плъгин за Eclipse, но също така работи самостоятелно за еднократни действия, има интеграции в Jenkins CI и други инструменти за изграждане

  2. По време на изпълнение:

    1. Задържане и ангажираност

      1. Ако задържането на ResultSet е ResultSet.CLOSE_CURSORS_OVER_COMMIT, тогава ResultSet се затваря, когато се извика методът Connection.commit(). Това може да се зададе с помощта на Connection.setHoldability() или чрез претоварения метод Connection.createStatement().
    2. Регистриране по време на изпълнение.

      1. Поставете добри отчети в регистрационния файл в кода си. Те трябва да са ясни и разбираеми, така че клиентът, помощният персонал и съотборниците да могат да разберат без обучение. Те трябва да са кратки и да включват отпечатване на състоянието/вътрешните стойности на ключовите променливи и атрибути, така че да можете да проследите логиката на обработката. Доброто регистриране е от основно значение за отстраняването на грешки в приложенията, особено тези, които са били внедрени.
      2. Можете да добавите JDBC драйвер за отстраняване на грешки към вашия проект (за отстраняване на грешки - всъщност не го внедрявайте). Един пример (не съм го използвал) е log4jdbc. След това трябва да направите прост анализ на този файл, за да видите кои изпълнения нямат съответното затваряне. Преброяването на отваряне и затваряне трябва да подчертае дали има потенциален проблем

        1. Наблюдение на базата данни. Наблюдавайте работещото си приложение с помощта на инструменти като функцията „Монитор на SQL“ на SQL Developer или TOAD на Quest. Мониторингът е описан в тази статия. По време на наблюдението вие заявявате отворените курсори (напр. от таблица v$sesstat) и преглеждате техния SQL. Ако броят на курсорите се увеличава и (най-важното) става доминиран от един идентичен SQL израз, знаете, че имате теч с този SQL. Потърсете кода си и прегледайте.

Други мисли

Можете ли да използвате WeakReferences за обработка на затварящи връзки?

Слабите и меките препратки са начини, които ви позволяват да препращате обект по начин, който позволява на JVM да събира боклука референта по всяко време, което сметне за подходящо (ако приемем, че няма силни референтни вериги към този обект).

Ако предадете ReferenceQueue в конструктора на меката или слабата Reference, обектът се поставя в ReferenceQueue, когато обектът е GC'ed, когато се появи (ако изобщо се появи). С този подход можете да взаимодействате с финализирането на обекта и можете да затворите или финализирате обекта в този момент.

Фантомните препратки са малко по-странни; тяхната цел е само да контролират финализирането, но никога не можете да получите препратка към оригиналния обект, така че ще бъде трудно да извикате метода close() върху него.

Въпреки това рядко е добра идея да се опитвате да контролирате кога се изпълнява GC (Слабите, Меките и PhantomReferences ви уведомяват след факта че обектът е поставен на опашка за GC). Всъщност, ако обемът на паметта в JVM е голям (напр. -Xmx2000m), може никога GC обекта и все още ще изпитате ORA-01000. Ако паметта на JVM е малка спрямо изискванията на вашата програма, може да откриете, че обектите ResultSet и PreparedStatement се GCed непосредствено след създаването (преди да можете да четете от тях), което вероятно ще провали програмата ви.

TL;DR: Слабият референтен механизъм не е добър начин за управление и затваряне на обектите Statement и ResultSet.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Изисквания за възстановяване преди архивиране

  2. PLSQL :НОВО и :СТАРО

  3. Oracle:Комбинирайте множество резултати в подзаявка в една стойност, разделена със запетая

  4. Функция LAST_DAY() в Oracle

  5. Oracle – Какъв файл с имена на TNS използвам?