[+/-]
In diesem Abschnitt wird beschrieben, wie Sie Connector/J in in unterschiedlichen Zusammenhängen einsetzen können.
Dieser Abschnitt vermittelt einige Grundkonzepte von J2EE, die für die Arbeit mit Connector/J von Bedeutung sind.
Verbindungspooling ist eine Technik, die darin besteht, einen Pool von Verbindungen aufzubauen und zu verwalten, damit diese für Threads, die sie benötigen, bereits zur Verfügung stehen.
Diese Technik beruht auf der Tatsache, dass die meisten Anwendungen nur dann Thread-Zugriff auf eine JDBC-Verbindung benötigen, wenn sie gerade aktiv eine Transaktion abwickeln, die in wenigen Millisekunden fertig ist. Wenn nicht gerade eine Transaktion verarbeitet wird, würde die Verbindung normalerweise untätig herumsitzen. In einem Verbindungspool kann sie dagegen von anderen Threads sinnvoll eingesetzt werden.
In der Praxis sieht das so aus: Wenn ein Thread auf MySQL oder eine andere Datenbank mit JDBC zugreifen muss, fordert er eine Verbindung aus dem Pool an. Hat er seine Arbeit auf der Verbindung beendet, gibt er sie an den Pool zurück, damit andere Threads sie wiederverwenden können.
Eine Verbindung, die aus dem Pool entliehen wurde, wird
ausschließlich von dem Thread benutzt, der sie angefordert
hatte. Aus Sicht der Programmierung ist dies dasselbe, als
riefe der Thread jedesmal, wenn ein eine JDBC-Verbindung
benötigt, DriverManager.getConnection()
auf. Beim Verbindungspooling kann er allerdings am Ende
entweder eine neue oder eine bereits vorhandene Verbindung
erwerben.
Verbindungspooling kann die Performance einer Java-Anwendung deutlich verbessern, da weniger Ressourcen verbraucht werden. Die Hauptvorteile des Verbindungspoolings sind:
Schnellerer Verbindungsaufbau
MySQL baut zwar im Vergleich zu anderen Datenbanken ohnehin Verbindungen sehr schnell auf, aber dennoch steigt bei der Aufnahme neuer JDBC-Verbindungen immer noch die Belastung für Netzwerk und JDBC-Treiber. Diese Belastung wird durch die Wiederverwendung von Verbindungen vermieden.
Einfacheres Programmiermodell
Durch das Verbindungspooling kann sich jeder einzelne Thread verhalten, als hätte er eine eigene JDBC-Verbindung. So können Sie ganz einfache JDBC-Programmiertechniken verwenden.
Bessere Kontrolle über den Ressourcenverbrauch
Wenn Sie auf Verbindungspooling verzichten und stattdessen jedesmal eine neue Verbindung öffnen, wenn ein Thread eine braucht, wird Ihre Anwendung unter Umständen viele Ressourcen unnötig belegen. Bei besonderer Belastung kann dies zu unvorhersehbarem Verhalten führen.
Bedenken Sie, dass jede Verbindung zu MySQL einen Aufwand bedeutet (für Arbeitsspeicher, CPU, Kontextumschaltungen usw.), und dies sowohl auf der Client- als auch auf der Serverseite. Mit jeder weiteren Verbindung schrumpfen die Ressourcen, die Ihrer Anwendung und dem MySQL-Server noch zur Verfügung stehen. Viele dieser Ressourcen werden ganz unabhängig davon belegt, ob die Verbindung gerade etwas Nützliches leistet oder nicht!
Verbindungspools können so getunt werden, dass die Performance möglichst gut ist, während gleichtzeitig die Ressourcenauslastung immer unter dem Niveau gehalten wird, ab dem die Anwendung nicht nur langsamer läuft, sondern sogar abstürzen kann.
Zum Glück hat Sun das Verbindungspooling in JDBC in den JDBC-2.0 Optional-Schnittstellen standardisiert, und alle wichtigen Anwendungsserver verfügen über Implementierungen dieser APIs, die mit MySQL Connector/J gut harmonieren.
Generell richten Sie den Verbindungspool in den Konfigurationsdateien Ihres Anwendungsservers ein und greifen über das Java Naming and Directory Interface (JNDI) darauf zu. Der folgende Code zeigt, wie ein Verbindungspool mit einer Anwendung auf einem J2EE-Anwendungsserver genutzt werden könnte:
Beispiel 25.11. Verwendung eines Verbindungspools mit einem J2EE-Anwendungsserver
import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import javax.naming.InitialContext; import javax.sql.DataSource; public class MyServletJspOrEjb { public void doSomething() throws Exception { /* * JNDI Initial-Kontext erstellen, um die * DataSource nachschlagen zu können * * In Produktionscode sollte dieser als Objekt oder statische Variable * gespeichert werden, da die Erstellung eines * JNDI-Kontexts sehr aufwändig sein kann. * * Hinweis: Der Code funktioniert nur, wenn Sie Servlets * oder EJBs in einem J2EE-Anwendungsserver verwenden. Nutzen Sie * Verbindungspooling in Standalone-Java-Code, so * müssen sie die Datenquellen mit den Mitteln erstellen/konfigurieren, * die Ihre jeweilige Bibliothek für Verbindungspooling * zur Verfügung stellt. */ InitialContext ctx = new InitialContext(); /* * Nachschlageoperation auf der DataSource mit einem vom Anwendungsserver * bereitgestellten Pool im Rücken. DataSource-Objekte sollten ebenfalls * als Instanzvariablen zwischengespeichert werden, * da auch JNDI-Lookups aufwändig sind. */ DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB"); /* * Der folgende Code würde eigentlich in die 'service'-Methode Ihres * Servlets, JSPs oder EJBs gehören, dorthin, wo * sie mit einer JDBC-Verbindung arbeiten müssen. */ Connection conn = null; Statement stmt = null; try { conn = ds.getConnection(); /* * Die weitere Arbeit mit MySQL wird mit normaler JDBC-Programmierung * erledigt. Schließen sie jede Ressource, wenn Sie sie nicht mehr benötigen, * damit die Verbindungspool-Ressourcen so rasch wie möglich * wiederhergestellt werden */ stmt = conn.createStatement(); stmt.execute("SOME SQL QUERY"); stmt.close(); stmt = null; conn.close(); conn = null; } finally { /* * Hier werden jdbc-Objekte abgeschlossen, die im normalen * Code nicht explizit geschlossen worden sind. So * entstehen keine Ressourcen-'Lecks'... */ if (stmt != null) { try { stmt.close(); } catch (sqlexception sqlex) { // ignorieren -- da wir hier sowieso nichts daran tun können } stmt = null; } if (conn != null) { try { conn.close(); } catch (sqlexception sqlex) { // ignorieren -- da wir hier sowieso nichts daran tun können } conn = null; } } } }
Wie das obige Beispiel zeigt, sieht der Rest des Codes, nachdem Sie den JNDI-InitialContext beschafft und die DataSource nachgeschlagen haben, für erfahrene JDBC-Programmierer schon vertrauter aus.
Das Wichtigste, was Sie sich merken müssen, wenn Sie mit Verbindungspooling arbeiten: Egal was in Ihrem Code passiert (Ausnahmen, Kontrollfluss usw.), sorgen Sie immer dafür, dass Verbindungen und alles, was mit ihnen angelegt wurde (Anweisungen, Ergebnismengen usw.) immer geschlossen werden. Sonst können sie nicht wiederverwendet werden und verlaufen im Sande. Im besten Fall bedeutet dies, dass die von ihnen belegten MySQL-Server-Ressourcen (Puffer, Sperren oder Sockets) für einige Zeit wegfallen, aber im schlimmsten Fall gehen sie für immer verloren.
Was ist die optimale Größe für meinen Verbindungspool?
Wie bei anderen Konfigurations-Faustregeln lautet auch hier die Antwort: Kommt drauf an. Zwar hängt die optimale Größe von der erwarteten Last und durchschnittlichen Datenbanktransaktionsdauer ab, aber die beste Größe für einen Verbindungspool kann kleiner sein als Sie erwarten. Wenn man die Java Petstore Blueprint-Anwendung von Sun als Beispiel nimmt, kann ein Pool von 15-20 Verbindungen für eine mäßige Last (600 Benutzer gleichzeitig) ausreichen. Mit MySQL und Tomcat wären die Reaktionszeiten in einem solchen Szenario annehmbar.
Um einen Verbindungspool für Ihre Anwendung angemessen zu dimensionieren, sollten Sie Belastungstestskripten mit Apache JMeter oder The Grinder erstellen und ausprobieren, wie sich Ihre Anwendung unter Last verhält.
Am einfachsten beginnen Sie wie folgt: Stellen Sie die Höchstzahl von Verbindungen für Ihren Pool auf unbegrenzt ein, lassen Sie einen Belastungstest laufen, und messen Sie, wie viele nebenläufige Verbindungen maximal gebraucht wurden. Von dort aus gehen sie einen Schritt zurück und schauen nach, welche Mindest- und Höchstzahlen an Pool-Verbindungen die beste Performance für Ihre Anwendung ergeben.
Die folgenden Hinweise beruhen auf den Anleitungen für Tomcat-5.x in http://jakarta.apache.org/tomcat/tomcat-5.0-doc/jndi-datasource-examples-howto.html Dies ist zum Zeitpunkt der Erstellung dieses Dokuments die aktuelle Version.
Zuerst installieren Sie die mit Connector/J mitgelieferte
.jar-Datei in $CATALINA_HOME/common/lib
,
sodass sie allen in dem Container installierten Anwendungen
zur Verfügung steht.
Nun konfigurieren Sie die JNDI-DataSource, indem Sie
$CATALINA_HOME/conf/server.xml
in dem
Kontext, der Ihre Webanwendung definiert, eine
Ressourcendeklaration hinzufügen:
<Context ....> ... <Resource name="jdbc/MySQLDB" auth="Container" type="javax.sql.DataSource"/> <!-- Muss _genau_ mit dem oben verwendeten Namen übereinstimmen! Der Verbindungspool wird mit dem Namen "java:/comp/env/jdbc/MySQLDB" in JNDI eingebunden --> <ResourceParams name="jdbc/MySQLDB"> <parameter> <name>factory</name> <value>org.apache.commons.dbcp.BasicDataSourceFactory</value> </parameter> <!-- Stellen Sie diesen Wert nicht höher als max_connections auf Ihrem MySQL-Server ein. Normalerweise sollte er 10 oder einige Dutzend Verbindungen vorsehen, nicht hunderte oder tausende --> <parameter> <name>maxActive</name> <value>10</value> </parameter> <!-- Sie möchten nicht zu viele untätige Verbindungen unnötig offen lassen, nur ein paar, um mögliche Belastungsspitzen zu absorbieren --> <parameter> <name>maxIdle</name> <value>5</value> </parameter> <!-- Bitte verwenden Sie nicht autoReconnect=true, erstens wird es irgendwann abgeschafft und zweitens ist es ein Kreuz für ältere Verbindungspools, die keine Verbindungen testen konnten. Sie müssen entscheiden, ob Ihre Verbindung SQLExceptions beherrschen soll (Hinweis: das sollte sie!) und wie viel Leistungseinbußen Sie hinnehmen wollen, um zu gewährleisten, dass die Verbindung "frisch" ist --> <parameter> <name>validationQuery</name> <value>SELECT 1</value> </parameter> <!-- Der konservativste Ansatz ist: Testen Sie Verbindungen, bevor Ihre Anwendung sie bekommt. Für die meisten Anwendungen ist das gut, außerdem ist die obige Anfrage ganz klein und belegt kaum Serverressourcen, abgesehen von der Zeit, die es braucht, sie im Netzwerk zu übermitteln. Wenn Sie eine Hochlast-Anwendung haben, werden Sie eine andere Lösung suchen müssen. --> <parameter> <name>testOnBorrow</name> <value>true</value> </parameter> <!-- Ansonsten, oder zusätzlich zu testOnBorrow, können Sie testen, während die Verbindung untätig ist --> <parameter> <name>testWhileIdle</name> <value>true</value> </parameter> <!-- Diesen Wert müssen Sie einstellen, sonst wird der Idle-Evicter-Thread nie ausgeführt, selbst wenn Sie veranlasst haben, dass untätige Verbindungen getestet werden --> <parameter> <name>timeBetweenEvictionRunsMillis</name> <value>10000</value> </parameter> <!-- Verbindungen sollten nie zu lange untätig herumsitzen, jedenfalls nicht länger als wait_timeout in der Serverkonfiguration. Einige Minuten oder Minutenbruchteile sind hier manchmal akzeptabel, es hängt von Ihrer Anwendung und den Belastungsspitzen ab --> <parameter> <name>minEvictableIdleTimeMillis</name> <value>60000</value> </parameter> <!-- Benutzername und Passwort für die MySQL-Verbindung --> <parameter> <name>username</name> <value>someuser</value> </parameter> <parameter> <name>password</name> <value>somepass</value> </parameter> <!-- Klassenname für den Connector/J-Treiber --> <parameter> <name>driverClassName</name> <value>com.mysql.jdbc.Driver</value> </parameter> <!-- Der JDBC-Verbindungs-URL für MySQL. Wenn Sie andere MySQL-spezifische Parameter üübergeben möchten, müssen Sie dies hier im URL tun. Sie in den obigen Parameter-Tags einzustellen, hätte keine Wirkung. Außerdem müssen Sie & als Trennzeichen zwischen die Parameterwerte setzen, da das Ampersand in XML ein reserviertes Zeichen ist. --> <parameter> <name>url</name> <value>jdbc:mysql://localhost:3306/test</value> </parameter> </ResourceParams> </Context>
Generell sollten Sie die Installationsanleitung zu Ihrer Tomcat-Version befolgen, da sich die Datenquellenkonfiguration in Tomcat von Zeit zu Zeit ändert. Leider wird, wenn Sie in Ihrer XML-Datei die verkehrte Syntax verwenden, eine Ausnahme wie diese angezeigt:
Error: java.sql.SQLException: Cannot load JDBC driver class 'null ' SQL state: null
Die folgenden Anleitungen beziehen sich auf JBoss-4.x. Um die
Klassen des JDBC-Teibers dem Anwendungsserver zur Verfügung
zu stellen, kopieren Sie die .jar-Datei von Connector/J in das
lib
-Verzeichnis für Ihre
Server-Konfiguration (normalerweise heißt es
default
). Dann legen Sie in demselben
Konfigurationsverzeichnis in einem Unterverzeichnis namens
deploy
eine
Datenquellenkonfigurationsdatei an, die mit "-ds.xml" endet.
Diese veranlasst JBoss, die betreffende Datei als
JDBC-Datasource einzusetzen. Die Datei sollte folgendes
enthalten:
<datasources> <local-tx-datasource> <!-- Dieser Verbindungspool wird in JNDI unter dem Namen "java:/MySQLDB" eingebunden --> <jndi-name>MySQLDB</jndi-name> <connection-url>jdbc:mysql://localhost:3306/dbname</connection-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <user-name>user</user-name> <password>pass</password> <min-pool-size>5</min-pool-size> <!-- Stellen Sie diesen Wert nicht höher als max_connections auf Ihrem MySQL-Server ein. Normalerweise sollte er 10 oder einige Dutzend Verbindungen vorsehen, nicht hunderte oder tausende --> <max-pool-size>20</max-pool-size> <!-- Verbindungen sollten nie zu lange untätig herumsitzen, jedenfalls nicht länger als wait_timeout in der Serverkonfiguration. Einige Minuten oder Minutenbruchteile sind hier manchmal akzeptabel, es hängt von Ihrer Anwendung und den Belastungsspitzen ab --> <idle-timeout-minutes>5</idle-timeout-minutes> <!-- Wenn Sie Connector/J 3.1.8 oder höher benutzen, können Sie unsere Implementierung verwenden, um die Stabilität des Verbindungspools zu erhöhen. --> <exception-sorter-class-name>com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter</exception-sorter-class-name> <valid-connection-checker-class-name>com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker</valid-connection-checker-class-name> </local-tx-datasource> </datasources>
Dies ist eine Übersetzung des MySQL-Referenzhandbuchs, das sich auf dev.mysql.com befindet. Das ursprüngliche Referenzhandbuch ist auf Englisch, und diese Übersetzung ist nicht notwendigerweise so aktuell wie die englische Ausgabe. Das vorliegende deutschsprachige Handbuch behandelt MySQL bis zur Version 5.1.