sql >> Databasteknik >  >> RDS >> Oracle

java.sql.SQLEundantag:- ORA-01000:maximala öppna markörer har överskridits

ORA-01000, felet med maximalt öppna markörer, är ett extremt vanligt fel i Oracle-databasutveckling. I Java-sammanhang händer det när applikationen försöker öppna fler resultatuppsättningar än vad det finns konfigurerade markörer på en databasinstans.

Vanliga orsaker är:

  1. Konfigurationsfel

    • Du har fler trådar i din applikation som frågar databasen än markörer på DB. Ett fall är när du har en anslutning och trådpool som är större än antalet markörer i databasen.
    • Du har många utvecklare eller applikationer kopplade till samma DB-instans (som förmodligen kommer att innehålla många scheman) och tillsammans använder du för många anslutningar.
    • Lösning:

      • Öka antalet markörer i databasen (om resurserna tillåter) eller
      • Minska antalet trådar i programmet.
  2. Markörläcka

    • Applikationen stänger inte ResultSets (i JDBC) eller markörer (i lagrade procedurer i databasen)
    • Lösning :Markörläckor är buggar; att öka antalet markörer på DB fördröjer helt enkelt det oundvikliga felet. Läckor kan hittas med statisk kodanalys, JDBC eller loggning på applikationsnivå och databasövervakning.

Bakgrund

Det här avsnittet beskriver en del av teorin bakom markörer och hur JDBC ska användas. Om du inte behöver veta bakgrunden kan du hoppa över detta och gå direkt till "Eliminera läckor".

Vad är en markör?

En markör är en resurs i databasen som håller tillståndet för en fråga, specifikt positionen där en läsare är i en resultatuppsättning. Varje SELECT-sats har en markör, och lagrade PL/SQL-procedurer kan öppnas och använda så många markörer som de behöver. Du kan ta reda på mer om markörer på Orafaq.

En databasinstans betjänar vanligtvis flera olika scheman , många olika användare var och en med flera sessioner . För att göra detta har den ett fast antal markörer tillgängliga för alla scheman, användare och sessioner. När alla markörer är öppna (i användning) och begäran kommer in som kräver en ny markör, misslyckas begäran med ett ORA-010000-fel.

Hitta och ställa in antalet markörer

Numret konfigureras normalt av DBA vid installation. Antalet markörer som används för närvarande, det maximala antalet och konfigurationen kan nås i administratörsfunktionerna i Oracle SQL Developer. Från SQL kan den ställas in med:

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

Relaterar JDBC i JVM till markörer på DB

JDBC-objekten nedan är tätt kopplade till följande databaskoncept:

  • JDBC Anslutning är klientrepresentationen av en databas-session och tillhandahåller databas-transaktioner . En anslutning kan bara ha en enda transaktion öppen åt gången (men transaktioner kan kapslas)
  • En JDBC Resultatuppsättning stöds av en enda markör på databasen. När close() anropas på ResultSet, släpps markören.
  • En JDBC CallableStatement anropar en lagrad procedur på databasen, ofta skriven i PL/SQL. Den lagrade proceduren kan skapa noll eller fler markörer och kan returnera en markör som en JDBC-resultatuppsättning.

JDBC är trådsäkert:Det är helt OK att skicka de olika JDBC-objekten mellan trådar.

Till exempel kan du skapa anslutningen i en tråd; en annan tråd kan använda denna anslutning för att skapa en PreparedStatement och en tredje tråd kan bearbeta resultatuppsättningen. Den enda stora begränsningen är att du inte kan ha mer än en ResultSet öppen på en enda PreparedStatement när som helst. Se Stöder Oracle DB flera (parallella) operationer per anslutning?

Observera att en databas commit inträffar på en anslutning, så alla DML (INSERT, UPDATE och DELETE's) på den anslutningen kommer att commit tillsammans. Därför, om du vill stödja flera transaktioner samtidigt, måste du ha minst en anslutning för varje samtidig transaktion.

Stänga JDBC-objekt

Ett typiskt exempel på att köra en ResultSet är:

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

Notera hur finally-satsen ignorerar alla undantag som skapas av close():

  • Om du bara stänger resultatuppsättningen utan försök {} catch {}, kan det misslyckas och förhindra att uttalandet stängs
  • Vi vill tillåta att alla undantag som tas upp i huvuddelen av försöket sprids till den som ringer. Om du har en loop över till exempel att skapa och köra uttalanden, kom ihåg att stänga varje uttalande i slingan.

I Java 7 har Oracle introducerat gränssnittet AutoCloseable som ersätter det mesta av Java 6-modellen med lite fint syntaktisk socker.

Håller JDBC-objekt

JDBC-objekt kan säkert hållas i lokala variabler, objektinstanser och klassmedlemmar. Det är generellt sett bättre praxis att:

  • Använd objektinstans eller klassmedlemmar för att hålla JDBC-objekt som återanvänds flera gånger under en längre period, som Connections och PreparedStatements
  • Använd lokala variabler för resultatuppsättningar eftersom dessa erhålls, loopas över och sedan stängs vanligtvis inom ramen för en enskild funktion.

Det finns dock ett undantag:Om du använder EJB:er eller en Servlet/JSP-behållare måste du följa en strikt gängningsmodell:

  • Endast applikationsservern skapar trådar (med vilka den hanterar inkommande förfrågningar)
  • Endast applikationsservern skapar anslutningar (som du får från anslutningspoolen)
  • När du sparar värden (tillstånd) mellan samtal måste du vara mycket försiktig. Lagra aldrig värden i dina egna cachar eller statiska medlemmar - detta är inte säkert för kluster och andra konstiga förhållanden, och applikationsservern kan göra hemska saker med din data. Använd istället stateful bönor eller en databas.
  • Särskilt aldrig håll JDBC-objekt (Connections, ResultSets, PreparedStatements, etc) över olika fjärranrop - låt applikationsservern hantera detta. Applikationsservern tillhandahåller inte bara en anslutningspool, den cachar även dina PreparedStatements.

Eliminera läckor

Det finns ett antal processer och verktyg tillgängliga för att hjälpa till att upptäcka och eliminera JDBC-läckor:

  1. Under utveckling - att fånga buggar tidigt är den absolut bästa metoden:

    1. Utvecklingsmetoder:Bra utvecklingsmetoder bör minska antalet buggar i din programvara innan den lämnar utvecklarens skrivbord. Specifika metoder inkluderar:

      1. Parprogrammering, för att utbilda dem utan tillräcklig erfarenhet
      2. Kod recensioner eftersom många ögon är bättre än ett
      3. Enhetstestning vilket innebär att du kan träna vilken kodbas som helst från ett testverktyg som gör det trivialt att återskapa läckor
      4. Använd befintliga bibliotek för anslutningspoolning istället för att bygga dina egna
    2. Statisk kodanalys:Använd ett verktyg som den utmärkta Findbugs för att utföra en statisk kodanalys. Detta plockar upp många ställen där close() inte har hanterats korrekt. Findbugs har ett plugin för Eclipse, men det körs också fristående för engångstillfällen, har integrationer i Jenkins CI och andra byggverktyg

  2. Vid körning:

    1. Hållbarhet och engagemang

      1. Om ResultSet-hållbarheten är ResultSet.CLOSE_CURSORS_OVER_COMMIT, stängs ResultSet när metoden Connection.commit() anropas. Detta kan ställas in med Connection.setHoldability() eller genom att använda den överbelastade metoden Connection.createStatement().
    2. Loggar vid körning.

      1. Sätt in bra loggsatser i din kod. Dessa bör vara tydliga och begripliga så att kunden, supportpersonalen och lagkamraterna kan förstå utan utbildning. De bör vara kortfattade och inkludera utskrift av tillstånd/interna värden för nyckelvariabler och attribut så att du kan spåra bearbetningslogik. Bra loggning är grundläggande för att felsöka applikationer, särskilt de som har distribuerats.
      2. Du kan lägga till en JDBC-drivrutin för felsökning till ditt projekt (för felsökning - distribuera inte den faktiskt). Ett exempel (jag har inte använt det) är log4jdbc. Du måste sedan göra en enkel analys av den här filen för att se vilka exekveringar som inte har en motsvarande stängning. Att räkna öppna och stänga bör markera om det finns ett potentiellt problem

        1. Övervakning av databasen. Övervaka din applikation som körs med hjälp av verktyg som SQL Developer 'Monitor SQL'-funktionen eller Quest's TOAD. Övervakning beskrivs i den här artikeln. Under övervakningen frågar du de öppna markörerna (t.ex. från tabellen v$sesstat) och granskar deras SQL. Om antalet markörer ökar och (viktigast av allt) domineras av en identisk SQL-sats vet du att du har en läcka med den SQL-koden. Sök efter din kod och granska.

Övriga tankar

Kan du använda WeakReferences för att hantera stängda anslutningar?

Svaga och mjuka referenser är sätt att tillåta dig att referera till ett objekt på ett sätt som gör att JVM kan sopsamla referenten när som helst den anser lämplig (förutsatt att det inte finns några starka referenskedjor till det objektet).

Om du skickar en ReferenceQueue i konstruktorn till den mjuka eller svaga referensen, placeras objektet i ReferenceQueue när objektet GC'ed när det inträffar (om det överhuvudtaget förekommer). Med detta tillvägagångssätt kan du interagera med objektets slutförande och du kan stänga eller slutföra objektet i det ögonblicket.

Fantomreferenser är lite konstigare; deras syfte är bara att kontrollera slutförandet, men du kan aldrig få en referens till det ursprungliga objektet, så det kommer att bli svårt att anropa close()-metoden på det.

Det är dock sällan en bra idé att försöka kontrollera när GC körs (Weak, Soft och PhantomReferences låter dig veta i efterhand att objektet är i kö för GC). Faktum är att om mängden minne i JVM är stor (t.ex. -Xmx2000m) kanske du aldrig GC objektet, och du kommer fortfarande att uppleva ORA-01000. Om JVM-minnet är litet i förhållande till ditt programs krav, kan du upptäcka att ResultSet- och PreparedStatement-objekten GCed omedelbart efter skapandet (innan du kan läsa från dem), vilket sannolikt kommer att misslyckas med ditt program.

TL;DR: Den svaga referensmekanismen är inte ett bra sätt att hantera och stänga Statement- och ResultSet-objekt.



  1. Hur man använder index för att förbättra MySQL-frågeprestanda

  2. Hur man formaterar datum och tid i SQL Server

  3. Vackra block av pannplåt

  4. Utforska lagringsmotoralternativ för MariaDB