MySQL MySQL Connection reset

yakazuqi

Mitglied
Guten Abend allerseits.
Ich habe zurzeit ein ziemlich nerviges Problem. Und zwar kommt nach circa 8 Stunden ein Fehler, wenn man zum Beispiel Werte setzt oder abfragt. Man kann wahrscheinlich die Dauer der vorhandenen Verbindung erhöhen. Aber selbst dann wird sie irgendwann schließen.

[CODE lang="java" title="MySQL Class"]@Getter
private Connection connection;

public void connect() {
try {
connection = DriverManager.getConnection("jdbc:mysql://" + HOST + ":" + PORT + "/" + DATABASE, USERNAME, PASSWORD);
} catch (Exception e) {
Main.log("§cThe connection to the database failed: " + e.getMessage());
}
}

public void createTable() {
connect();
update("CREATE TABLE IF NOT EXISTS SpeedCW (UUID CHAR(36), USERNAME VARCHAR(16), KILLS INT(10), DEATHS INT(10), PLAYED INT(10), WINS INT(10), BED INT(10), LOSES INT(10), WONGAMEWITHDESTORYBED INT(10), PRIMARY KEY(UUID))");
update("CREATE TABLE IF NOT EXISTS Achievements (UUID CHAR(36), USERNAME VARCHAR(16), FIRSTTIME INT(1), BUSINESSMAN INT(1), ELITE INT(1), FAIRPLAY INT(1), FIRSTBLOOD INT(1), OVERPOWERING INT(1), WINNER INT(1), SAGITTARIUS INT(1), BEAM INT(1), BOWKILL INT(1), CLUMSY INT(1), PRIMARY KEY(UUID))");
}

public void disconnect() {
if(isConnected()) {
try {
connection.close();
} catch (Exception e) {
}
}
}

private boolean isConnected() {
return connection != null;
}

public void update(String query) {
if(connection != null) {
try {
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.executeUpdate();
preparedStatement.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}[/CODE]

[CODE lang="java" title="Mit dieser Methode hol ich mir die Werte"]public Integer getKills(OfflinePlayer player) {
try {
PreparedStatement preparedStatement = Main.getInstance().getMySQL().getConnection().prepareStatement("SELECT * FROM SpeedCW WHERE UUID = ?");
preparedStatement.setString(1, player.getUniqueId().toString());
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
return resultSet.getInt("KILLS");
}
resultSet.close();
preparedStatement.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
return 0;
}[/CODE]

Mit freundlichen Grüßen
 
Also meine Meinung ist hier relativ deutlich, aber halt von meiner .Net Entwicklung her geprägt:

Connection Pools! Das ist da der Standard und wird eigentlich immer genutzt. Apache Commons DBCP wäre bei java dann zu nennen als eine Lösung:

Der Ablauf ist dann immer gleich:
- DriverManger wird um eine Connection gebeten.
- Die Connection wird genutzt
- Am Ende wird die Connection geschlossen.

Und natürlich kommt auch eine Begründung - dazu schauen wir uns einfach die genannten anderen Lösungen an:

a) "PING"-Lösung. Sorry, aber das ist hoffentlich nicht ernst gemeint. Die Connection kann ja auch aus anderen Gründen geschlossen worden sein.
b) autoReconnect ist gut. Das macht dann das, was man auch manuell machen kann: Prüfen, ob die Connection da ist um diese dann ggf. zu erneuern. Wenn man dies manuell machen würde, wäre der erste Schritt, dass man das isConnected verbessert: isValid(int) müsste da als Aufruf mit dazu genommen werden ...

Das sieht nach einer brauchbaren Lösung aus, ABER:
Sobald es zu mehreren Threads kommt, hat man ein Problem. JDBC Treiber sollten zwar thread safe aufgebaut sein, aber das bedeutet nicht, dass da über eine Connection mehrere Befehle gleichzeitig abgesetzt werden. Es blockiert dann einfach nur ein Thread, bis der andere fertig ist. Also nicht gut.

Daher: Das Problem kann man lösen, indem man Connections vor dem Befehl erst holt und nach der Nutzung direkt freigibt. Und um die ständigen Verbindungsaufbauten nicht zu haben (Wenn man nur hin und wieder mal eine Connection braucht, wäre das ggf. sogar schon eine Lösung ohne Connection Pool!) nutzt man einen Connection Pool.

Edit: fehlendes t eingefügt - Tastatur mag nicht mehr ...
 
a) "PING"-Lösung. Sorry, aber das ist hoffentlich nicht ernst gemeint. Die Connection kann ja auch aus anderen Gründen geschlossen worden sein.
Der Ping-Request ist gerade dazu da, die Connection zu validieren (s. Link oben). Das macht ein Connection Pool nicht anders, oder andersrum gesagt: auch beim Connection Pool zieht MySQL einer offene Connection irgendwann den Boden unter den Füßen weg. Gerade dbcp2 löst dieses Problem m. W. nicht, c3p0 hat dagegen einen Validator. Eine Alternative wäre, immer eine frische Connection zu verwenden. Dann braucht man sich aber über Connection Pooling erst recht keine Gedanken mehr zu machen. Statt regelmäßigen Pings kann die Validierung natürlich auch im Rahmen des Exception-Handlings erfolgen, um festzustellen, ob eine neue Verbindung aufgebaut werden muss.

Was autoReconnect betrifft: "The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don't handle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead and stale connections properly." (https://dev.mysql.com/doc/connector...nd-clustering.html#cj-conn-prop_autoReconnect)
 
Ja, es wist wohl wieder nur eine Wortwahl, die etwas aufgefallen ist, denn es braucht doch etwas mehr als eben nur das "Mach von Zeit zu Zeit einen Ping-Request". Das klang halt mehr nach "schick einfach nur hin und wieder so einen Request, damit die Verbindung nicht geschlossen wird.

Da geht es ja darum, eine Verbindung zu validieren, ehe sie benutzt wird.

Also statt:
Java:
@Getter
private Connection connection;

eher etwas wie (Vereinfacht gezeigt):
Java:
private Connection connection;

public Connection getConnection() {
    if (!isValid()) {
        connect();
        if (!isValid()) throw new DatabaseNotAvailableException();
    }
    return connection;
}

Also den Sinn eines PING Requests zur Validierung bestreite ich nicht. Aber der alleine bringt nicht viel. Der mag ein Timeout verhindern aber es ändert nichts an der generellen Thematik.

auch beim Connection Pool zieht MySQL einer offene Connection irgendwann den Boden unter den Füßen weg. Gerade dbcp2 löst dieses Problem m. W. nicht
Öhm - meine Erwartung beim ConnectionPool ist gerade, dass immer eine Connection geliefert wird, die valide ist, d.h. dass der ConnectionPool den Status entsprechend prüft. Oder meinst Du, dass eine Connection, die man von einem ConnectionPool bekommt, auch einen Timeout haben kann?
Ich muss gestehen, dass ich mich bei Java in erster Linie auf die vorhandenen Libraries stütze und so Themen anderen Libraries überlasse. Aber da interessiert mich nun wirklich: Kann es sein, dass mir dbcp2 eine Connection gibt, die nicht valide ist? Sprich ich habe dann Code wie
Java:
DataSource dataSource = setupDataSource(.....);
Connection conn = dataSource.getConnection();
// kann conn hier nicht valide sein?

Die Dokumentation besagt hier nichts - es wird eine Connection zurück gegeben ... Aber meine Erwartungshaltung wäre, dass entweder eine freie Connection aus dem Pool geprüft wird (isValide(...) ist damit true!) oder wenn es so eine Connection nicht gibt, dass dann eine neue Connection erstellt wird.
 
Das Problem hatten wir vor x Jahren mit einem Connection Pool schon gehabt. Wobei Du mir gerade die Betriebsblindheit genommen hast:
meine Erwartung beim ConnectionPool ist gerade, dass immer eine Connection geliefert wird, die valide ist, d.h. dass der ConnectionPool den Status entsprechend prüft.
Und auch der Ping-Request sollte vom Treiber bei isValid() abgesetzt werden. Au, das ist ja peinlich. Ich nehme alles zurück und behaupte das Gegenteil 🙂

Danke, für diesen erleuchtenden Moment.
 
b) autoReconnect ist gut. Das macht dann das, was man auch manuell machen kann: Prüfen, ob die Connection da ist um diese dann ggf. zu erneuern.
Meines Wissens nach ist autoReconnect veraltet und wird kaum noch genutzt.

Wie wäre es damit?
[CODE lang="java" title="isConnected"]private boolean isConnected() {
try {
return (connection != null && connection.isValid(5));
} catch (SQLException exception) {
exception.printStackTrace();
}
}[/CODE]
 
Zuletzt bearbeitet:
Meine bevorzugte Lösung ist und bleibt der Connection Pool. Dein Code könnte aber eine Basis darstellen. Du braucht natürlich einen Code bei getConnection, der das checkt und dann ggf. eine neue Connection erstellt.

Edit: musst natürlich den Code noch so anpassen, dass er compilert (return false im catch z.B.)
 

Zurück
Oben