Zum Inhalt springen

MySQL-Connect mittels Connection Pooling


Empfohlene Beiträge

Geschrieben

Hallo,

ich habe eine grundlegende Frage zur Arbeit mit Java und MySQL.

Ich habe eine Klasse Connect, die folgendes Grundgerüst aufzeigt:


		pc = null;

		MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();

		try {


			// Connection

			ds.setUrl(ReadConfig.Element("database_connectionstring"));

			ds.setUser(ReadConfig.Element("database_user"));

			ds.setPassword(ReadConfig.Element("database_pass"));

			// Reconnect

			ds.setAutoReconnect(true);

			ds.setAutoReconnectForConnectionPools(true);

			ds.setAutoReconnectForPools(true);


			pc = ds.getPooledConnection();

		} catch (SQLException e) {


			new Exception(e.toString());

		}

Und eine weitere Funktion getConnection, die die Member-Variable pc zurück gibt.

		try {


			return pc.getConnection();

		} catch (Exception e) {


			connect();


			try {


				return pc.getConnection();

			} catch (Exception ex) {


				new Exception(e.toString());

				return null;

			}

		}

Sobald ich eine Abfrage an die Datenbank stellen möchte, sieht der Ablauf folgendermaßen aus:

			Connection con = Connect.getConnection();

			this.stmnt = con.createStatement();

                        ResultSet rs = this.stmnt.executeQuery("SELECT hose FROM kleidung WHERE name='Fritz'");

                        while(rs.next()) {

(...)

}

Wenn ich also eine Abfrage absende, erstelle ich jedesmal ein neues ResultSet, das auf (in diesem Falle) this.stmnt zurück greift.

Nun habe ich - wenn ich mehrere Abfragen in der Sekunde absende - einen Engpass, der sich folgendermaßen äußert:

java.sql.SQLException: Unexpected exception encountered during query.

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

Last packet sent to the server was 12345ms ago.

Manchmal kommt auch folgendes Szenario zustande:

java.sql.SqlException: Logical handle no longer valid

com.mysql.jdbc.PacketTooBigException: Packet for query is too large (6644739 > 1047552). You can change this value on the server by setting the max_allowed_packet variable.

Jetzt frage ich mich, wie ich bei vielleicht 30-40 Abfragen (SELECT-Abfragen, alle WHEREs, GROUPs und ORDERs sind vernünftig indiziert) in der Sekunde so einen Engpass bekomme. Es handelt sich dabei um Abfragen über zwei Tabellen. Die eine zählt knapp 3.000 Einträge (wobei zwei Spalten ausgelesen werden) und eine weitere zählt knapp 5.000 Einträge, wo lediglich eine Bedingung auf eine Spalte der Tabelle besteht.

Gehe ich die Abfrage nun generell falsch an oder wo kann dort mein Engpass liegen?

Das grundlegende Problem dabei ist, dass ich nach Erhalt der Exceptions keine weiteren Abfragen mehr an die Datenbank senden kann. Erst., wenn ich den MySQL-Dienst neustarte, ist dies wieder möglich.

Ich hoffe mir kann jemand helfen, auch gerne mit etwas guter Lektüre im Internet. Ich weiß nämlich nicht mehr, was ich noch weiter machen soll/kann.

MFG KK

Geschrieben

Ich vermute, dass Du immer auf der selben Connection arbeitest, und dabei Threading-Probleme bekommst.

Ich habe mit der MysqlConnectionPoolDataSource noch nicht direkt gearbeitet, weil mir das Connection Pooling immer schön ein Container oder c3p0 macht, aber die Fehler hören sich danach an. Du verwendest auch die Methode getConnection() und nicht getPooledConnection().

Schöne Grüße,

Peter

Geschrieben

Hallo kingofbrain,

dass Du immer auf der selben Connection arbeitest, und dabei Threading-Probleme bekommst.

Du verwendest auch die Methode getConnection() und nicht getPooledConnection().

Also würdest du sagen, dass die Funktion getConnection besser ds.getPooledConnection() zurück gibt, als ds.getPooledConnection().getConnection()? (pc wird erstellt mit ds.getPooledConnection(), also im Prinzip pc.getConnection())

Das Projekt C3P0 war mir bislang nicht geläufig. Hätte ich dadruch denn weitere Vorteile, die mir bei meinem jetzigen Problem fehlen?

MFG KK

Geschrieben

Hi,

ich glaub Du hast hier einen recht großen Denkfehler drinnen

MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();

Du legst ein lokales Objekt an. Nach Beendigung der Methode ist es weg und das entspricht nicht so ganz dem Grundgedanken des Poolings. Bevor das Poolingobjekt gelöscht wird holst Du dir eine Connection raus und speicherst sie in einem nicht näher genannten Objekt rc.

Zum einen darf der Pool keine lokales Objekt sein, zum anderen solltest Du nur eine Methode haben, die aus dem Pool eine Connection zurückgibt und nicht mehr. Warum du das nochmal zwischenspeicherst kann ich nicht nachvollziehen.

Nachdem Du die Connection nicht mehr brauchst close aufrufen und sie wandert wieder in den Pool zurück.

Dim

Geschrieben

Hallo ihr zwei,

ich habe das Ganze nochmals grundlegend überarbeitet und setze jetzt auf c3p0. Ebenfalls ist mir der (wirklich dumme....) Denkfehler sauer aufgestoßen, sodass ich diesen ebenfalls bereinige.

Jetzt stellt sich mir noch eine weitere Frage: Ist es sinnig, in jeder Funktion, die auf die Connection zugreift - also jedesmal, wenn ich eine Abfrage an die Datenbank sende - ein


Statement stmnt = new Connect.getConnection();

ResultSet rs = stmnt.createStatement();

rs.executeQuery("SELE...");

zu machen, wobei getConnection nun die Connection von ComboPooledDataSource zurück gibt?! Oder würde es Sinn machen, in einer Datenbank-Klasse eine Funktion executeQuery(queryString) zu erstellen, die auf dasselbe Statement-Objekt zurück greift und entsprechend nur jedesmal ein

return rs.executeQuery(sql);

zurück gibt?

MFG KK

Geschrieben

Hallo,

ich bin nun wiefolgt wiederfahren:


	private static ComboPooledDataSource ds = null;

	private static Connection con = null;

	private static boolean db_connected = false;


	public static void connect()

	{

		if (isConnected())

		{

			System.out.println("is connected");

			return;

		}

		else

		{ 

			System.out.println("is not connected");

		}


		try

		{

			ds = new ComboPooledDataSource(); 

			ds.setDriverClass("org.gjt.mm.mysql.Driver");

			ds.setJdbcUrl(ReadConfig.Element("database_connectionstring")); 

			ds.setUser(ReadConfig.Element("database_user"));

			ds.setPassword(ReadConfig.Element("database_pass"));

			con = ds.getConnection();

			db_connected = true;

		} 

		catch (Exception e)

		{

			System.out.println(e.toString());

			db_connected = false;

		}

	}


	public static void distroyConnection()

	{

		try

		{

			DataSources.destroy(ds);

			db_connected = false;

		}

		catch (Exception e)

		{

			System.out.println(e.toString());

		}

	}


	public static ResultSet executeQuery(String sql)

	{

		try {

			System.out.println("users: " + ds.getNumUserPools());

			System.out.println("cons: " + ds.getNumConnections());

		} catch (SQLException e1) {

			// TODO Auto-generated catch block

			e1.printStackTrace();

		}

		try

		{

			return con.createStatement().executeQuery(sql);

		}

		catch (SQLException e)

		{

			try

			{

				connect();

				return con.createStatement().executeQuery(sql);

			} 

			catch (Exception e2)

			{

				System.out.println(e2.toString());

				return null;

			}

		}

	}


	public static boolean isConnected()

	{

		try

		{

			ds.getConnection();

			db_connected = true;

		}

		catch (Exception e)

		{

			db_connected = false;

		}

		return db_connected;

	}

Ich führe also nun bei jeder Abfrage an die Datenbank meine Funktion executeQuery auf, die jedesmal ein Statement erstellt und ein ResultSet zurück gibt. Komisch ist nur, dass "System.out.println("cons: " + ds.getNumConnections());" bei einem Client 3 zurück gibt und bei einem zweiten 6. Wobei 6 Connections auch das Maximum sind. Kommt ein dritter Client hinzu, bleibt es bei 6 aktiven Connections. Eine Connection wird nie geschlossen bei mir, da die Applikation dauerhaft in einem Tomcat läuft. Die Funktion distroyConnection wird also nie aufgerufen. Sobald ich ein Statement bloß einmal erstelle (bsp)

			ds = new ComboPooledDataSource(); 

			ds.setDriverClass("org.gjt.mm.mysql.Driver");

			ds.setJdbcUrl(ReadConfig.Element("database_connectionstring")); 

			ds.setUser(ReadConfig.Element("database_user"));

			ds.setPassword(ReadConfig.Element("database_pass"));


                        stmnt = ds.getConnection().createStatement();


			db_connected = true;

kann ich auch bloß eine Abfrage gleichzeitig an die Datenbank senden. Überlappen sich zwei Abfragen (durch zwei gleichzeitig agierende Clients), so bekomme ich die Exception "Operation not allowed after ResultSet closed".

Also generiere ich für jede Abfrage ein neues Statement und bekomme ein neues ResultSet zurück.

Gibt es hierzu noch Anregungen/Anmerkungen oder Fragen?

MFG KK

Geschrieben

Hallo,

warum lässt Du dann das ganze Connection-Gerümpel nicht auch den Tomcat machen?

Weil ich dann immernoch nicht weiß, wie der ganze Kram vernünftig funktioniert ;)

Komisch ist nur, dass "System.out.println("cons: " + ds.getNumConnections());" bei einem Client 3 zurück gibt und bei einem zweiten 6. Wobei 6 Connections auch das Maximum sind. Kommt ein dritter Client hinzu, bleibt es bei 6 aktiven Connections.

Da kann ich mich selbst zitieren und die Frage beantworten

initialPoolSize

Default: 3

Number of Connections a pool will try to acquire upon startup. Should be between minPoolSize and maxPoolSize. [see "Basic Pool Configuration"]

Ich hoffe nun mit c3p0 besser zu fahren, als zuvor. Es macht augenscheinlich ( nicht ausgiebig getestet ) einen sehr sehr vernünftigen Eindruck. Bislang keine Exceptions. Und auch die Performance lässt einiges offen....

Ich bedanke mich bei euch!

MFG KK

Geschrieben
Weil ich dann immernoch nicht weiß, wie der ganze Kram vernünftig funktioniert

Wie die Interna von c3p0 das Connection Pooling machen, weißt Du jetzt ja auch nicht. Wenn Du nicht gerade einen Connection Pool programmieren willst, dann ist das auch ziemlich egal. Mit einem JNDI-Lookup erhältst Du immer das passende Objekt (DataSource) und kannst vernünftig arbeiten. Der Pool verwaltet sich automagisch. :)

Aber wenn es Dir nur ums lernen geht, dann mach nur. Wenn Du es durchschaut hast, kannst Du ja immer noch die Komfortmöglichkeit nutzen.

Schöne Grüße,

Peter

Geschrieben

Also das ganze ist nicht viel besser als das von vorher. Ein Connectionpool ist dazu da dir Connections zu geben und nicht dazu intern eine zu verstecken.

public static synchronized Connection getConnection()

{

  if(isConnected)

    return deineDSSource.getConnection();

  else

  {

     Verbindung aufbauen und dann die Connection zurückgeben

  }

}
Im deinem Programmcode:

public void deineFunktion()

{

  Connection con=null;

  try

  {

    //Was auf der DB machen

   con=MeinPool.getConnection();

  }

  catch(...){ ... }

  finally

  {

  ...

  if(con!=null)

    con.close(); //<---Hier wandert die Connection wieder in den Pool zurück

  }

}
private static Connection con = null;
Brauchst Du nicht. Der Pool hält die Connections und gibt sie nach außen. In der Klasse selbst hat es keine Connections zu geben. Dann das hier:
try {

			System.out.println("users: " + ds.getNumUserPools());

			System.out.println("cons: " + ds.getNumConnections());

		} catch (SQLException e1) {

			// TODO Auto-generated catch block

			e1.printStackTrace();

Wird das hier ausgeführt bevor Du connect() aufgerufen hast bekommst Du eine NullPointerException. Damit ist auch Deine zweite try catch Konstruktion ad absurdum geführt, denn soweit wirst Du ohne vorherigen connect nie kommen.

Komisch ist nur, dass "System.out.println("cons: " + ds.getNumConnections());" bei einem Client 3 zurück gibt und bei einem zweiten 6. Wobei 6 Connections auch das Maximum sind. Kommt ein dritter Client hinzu, bleibt es bei 6 aktiven Connections.

Das ist nicht komisch, das ist der Sinn und Zweck eines ConnectionPools! Die Zeit die nötig ist eine Connection aufzubauen wird nur einmal benötigt. Danach wandert die Connection wieder in den Pool zurück und kann wiederverwendet werden. Verwechsle das nicht mit einer Factory, die dir Connections erzeugt die eigenständig sind und dann wieder geschlossen werden.

Eine Connection wird nie geschlossen bei mir, da die Applikation dauerhaft in einem Tomcat läuft.
Beachte auch die besonderheiten, die man beim SingletonPattern u.a. in einem Container berücksichtigen muss.

Überlappen sich zwei Abfragen (durch zwei gleichzeitig agierende Clients), so bekomme ich die Exception "Operation not allowed after ResultSet closed".

Hau die executeQuery Methode raus. Das hat in einem Pool nichts zu suchen. Des weiteren müssen statische Methoden in einer Multithreading Umgebung zwingen per synchronize gegen konkurierende Zugriffe geschützt werden.

Dim

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...