2017-03-23 3 views
0

Ich benutze JDBC-Pool für meine Tomcat-Web-Anwendungen.JDBC Pool - Wie man richtig verwendet

Wie ich in Dokumenten lesen, brauche ich Verbindung, führen Abfrage und schließen. Nach dem Schließen kehrt die Verbindung zum Pool zurück.

Ich denke Weg 1 besser, wenn Abfrage DB von externen Ereignissen abhängen und für die Aufgabe, die alle 5 sec Way 2 mehr korrekt.

Kann jemand erklären, welche Art der Verwendung für Aufgaben, die alle 5 Sekunden wiederholen?

PS: Ich überspringe zusätzliche Checks in Code, um Code lesbar zu machen.

Way # 1 Ruft Verbindung von Pool und in der Nähe Looping

Connection c = null; 
Statement s = null; 
ResultSet rs = null; 
DataSource ds = ... Get DataSource ... 

c = ds.getConnection(); 

while(running) { 
    try { 
     s = c.createStatement(); 
     rs = s.executeQuery('SELECT data FROM my_table'); 
     ... do something with result ... 
    } catch (SQLException sec) { 
     ... print exception ... 
     ... if connection lost, try reconnect and execute query again ... 
    } finally { 
     try { 
      rs.close(); 
      s.close(); 
     } catch (SQLException sec) { 
      ... print exception ... 
     } 
     ... Thread sleep 5 seconds and repeat ... 
    } 
} 

c.close(); 

Pool Config alle 5 Sekunden

Connection c = null; 
Statement s = null; 
ResultSet rs = null; 
DataSource ds = ... Get DataSource ... 

while(running) { 
    try { 
     c = ds.getConnection(); 
     s = c.createStatement(); 
     rs = s.executeQuery('SELECT data FROM my_table'); 
     ... do something with result ... 
    } catch (SQLException sec) { 
     ... print exception ... 
    } finally { 
     try { 
      rs.close(); 
      s.close(); 
      c.close(); 
     } catch (SQLException sec) { ... print exception ... } 

     ... Thread sleep 5 seconds and repeat ... 
    } 
} 

Way # 2 Get Verbindung vor Schleife und schließen danach wieder

<Resource name="jdbc/pg_mega" auth="Container" 
     type="javax.sql.DataSource" driverClassName="org.postgresql.Driver" 
     url="jdbc:postgresql://127.0.0.1:6432/db" 
     factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" 
     username="***" password="****" 
     defaultAutoCommit="true" 
     initialSize="1" 
     maxActive="300" 
     maxTotal="300" 
     maxIdle="20" 
     minIdle="5" 
     maxWait="10000" 
     validationQuery="select 1" 
     validationInterval="30000" 
     testWhileIdle="false" 
     testOnBorrow="true" 
     testOnReturn="false" 
     timeBetweenEvictionRunsMillis="30000" 
     minEvictableIdleTimeMillis="30000" 
     /> 
+0

Ich verstehe nicht, warum Sie Thread-Schlaf auf Web-App haben, es sei denn, Sie verarbeiten große Daten oder Async-Prozess? Der zweite Ansatz sieht gut aus. Sie müssen keine Verbindung bei jeder Iteration der Schleife erhalten. – notionquest

+0

Dieser Code von Bean, die Statistik, Teil der Web-App aggregieren – Dmitry

+2

Nun, die zweite Option reserviert eine Verbindung vollständig für diesen Zweck. Der erste ermöglicht anderen Threads, die Verbindung zu verwenden, während der Thread sie nicht verwendet. Es macht wahrscheinlich keinen großen Unterschied. Sie sollten jedoch einen richtigen Scheduler anstelle eines While/Sleep-Systems verwenden. – Kayaman

Antwort

1

Ich denke, die häufigste Muster ist dies:

Connection conn = null; 
    PreparedStatement stmt = null; 
    ResultSet res = null; 
    try { 
     conn = ds.getConnection(); 
     stmt = conn.prepareStatement(sqlStatement); 
     // 
     // .... 
     res = stmt.executeQuery(); 

     // use the resultset 

     conn.commit(); 
    } catch (SQLException e) { 
     // Manage the exception 
     try { 
      conn.rollback(); 
     } catch (SQLException e1) { 
      // SWALLOW 
     } 

    } finally { 
     close(res); 
     close(stmt); 
     close(conn); 
    } 

Ich benutze diese Funktionen Helfer, um sicher zu schließen, ohne zu viel Textvorschlag, von Java 7 auf Sie so diese Helfer sind mehr keine Verwendung Autoclose kann.

public static void close(Connection conn) { 
    try { 
     if (conn != null) 
      conn.close(); 
    } catch (SQLException e) { 
     // SWALLOW 
    } 
} 

public static void close(Statement stmt) { 
    try { 
     if (stmt != null) 

      stmt.close(); 
    } catch (SQLException e) { 
     // SWALLOW 
    } 
} 

public static void close(ResultSet res) { 
    try { 
     if (res != null) 
      res.close(); 
    } catch (SQLException e) { 
     // SWALLOW 
    } 
} 

Sie sollen sich wirklich sicher sein, die Verbindung in der finally-Anweisung, schlechte Dinge passieren, wenn Sie nicht die Verbindung schließen, und wenn (zum Beispiel) die rs ist null in Ihrem Beispiel (nicht so schwer) schließen Sie werden die Verbindung nicht schließen.

Das Abrufen und Freigeben der Verbindung aus dem Pool ist kein Leistungsproblem, es tritt in Mikrosekunden auf, tausende Male schneller als jede mögliche Abfrage.

Der Grund, warum Sie die Verbindung nicht bereitwillig freigeben, ist Transaktionen, Sie möchten dieselbe Verbindung für die gesamte Transaktion behalten (umsonst).

Beim Commit (oder Rollback) brauchst du diese merkwürdige Verbindung nicht mehr, also lass sie einfach los.

Ein weiterer Hinweis, schließen Sie die Verbindung schließlich, auch wenn Sie SQL-Ausnahmen fangen, weil es immer Runtime-Ausnahmen und sogar Fehler (die Sie nicht fangen), aber schließlich auch angesichts OutOfMemoryError oder ClassDefNotFoundError ausgeführt wird oder andere, und die Verbindung wird zum Pool zurückgegeben.

Last but not least ist der Pool, der versuchen wird, im Falle einer Verbindungsunterbrechung die Verbindung wiederherzustellen, tatsächlich wird der Pool eine ungültige Verbindung abbrechen und bei Bedarf eine neue Charge erstellen.

Sie sollten eine gute Richtlinie für die Verbindungsvalidierung wählen, eine schlechte Wahl führt zu viel zusätzlicher Zeit, um die Verbindung zu erhalten und somit die Leistung zu beeinträchtigen, oder zu Ausnahmen, die durch ungültige Verbindung aus dem Pool verursacht werden.

Die Optimierung des Pools ist wie bei vielen anderen Aufgaben zur Leistungsoptimierung: HARD.

Zum Beispiel: testOnBorrow="true" wird die DB vor dem Erwerb der Verbindung treffen, ist es sicher, aber es wird kosten, Zehner oder Hunderte von Zeit langsamer, dass nicht auf Borrow zu überprüfen.

testWhileIdle="true" ist weniger sicher (Sie könnten eine ungültige Verbindung erhalten), ist aber viel schneller und hat den Vorteil, die Verbindungen aufrecht zu erhalten.

Sie müssen wählen, wie Sie Verbindung verwenden, wie Sie mit Fehlern umgehen, wo die DB ist (auf der gleichen Maschine, auf einem LAN, auf einem WAN) und viele andere Faktoren.

1

Weg # 2 ist nicht korrekt, wenn Sie einen Pool verwenden. Wenn Sie einen Pool verwenden, sollten Sie immer versuchen, die Verbindung so kurz wie möglich aus dem Pool ("geleast") herauszuhalten, um die Poolnutzung optimal zu nutzen. Wenn Sie keinen Pool verwenden, müssen Sie die Kosten für das Erstellen und Zerstören von Verbindungen und das Verwalten des Lebenszyklus der Verbindung berücksichtigen.

Wenn Sie einen Pool verwenden, muss eine gemietete Verbindung immer an den Pool zurückgegeben werden (beim Schließen der Verbindung wird eine Verbindung zum Pool zurückgegeben). Wenn Verbindungen nicht zum Pool zurückgegeben werden (d. H. Verbindungen werden durchgesickert), ist der Pool bald leer und Ihre Anwendung wird nicht mehr funktionieren. Dies ist besonders wichtig, wenn etwas schief geht (z. B. in Ihrem Codebeispiel, wenn rsnull aufgrund eines Abfragefehlers ist, wird die Verbindung durchgesickert). Um zu verhindern, dass Verbindungen undicht werden, sollten Sie ein Werkzeug wie Sql2o verwenden, das einen eingebauten Schutz gegen Verbindungsverlust aufweist.

Reduzieren Sie auch die Anzahl der Verbindungen im Pool. Beginnen Sie mit minIndle="1" und maxActive="4". Verwenden Sie Stresstests, um die Obergrenze der Poolgröße zu bestimmen (mehr Verbindungen in einem Pool verursachen normalerweise mehr Schaden als Nutzen, siehe auch About Pool Sizing von HikariCP, die mehr gute Artikel über Datenbankverbindungspools enthält).