aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Game-Server

MySQL Connection Pool: Leitfaden zur Verbindungsverwaltung

Auf einem ausgelasteten Gameserver oder einer Webanwendung mit viel Traffic ist ein MySQL Connection Pool (Verbindungspool) eine der günstigsten Möglichkeiten, die Leistung zu steigern. Der Grund: Eine neue Verbindung für jede Datenbankoperation zu öffnen und zu schließen ist weit teurer, als es aussieht — ein TCP-Handshake, Authentifizierung, Zeichensatz-Aushandlung und die Zuweisung eines Threads auf Serverseite. Wird all das bei jeder Anfrage wiederholt, verbringst du am Ende mehr Zeit und CPU mit dem Aufbau als mit den eigentlichen Abfragen. Dieser Artikel erklärt, was ein Verbindungspool ist, warum und wie er die Serverlast senkt und wie man ihn in verschiedenen Sprachen korrekt einrichtet.

Warum ist das Öffnen einer Verbindung teuer?

Wenn du mysql_real_connect aufrufst oder ein neues PDO-Objekt erstellst, passiert hinter den Kulissen Folgendes:

  • Zwischen Client und Server wird eine TCP-Verbindung aufgebaut (Drei-Wege-Handshake; zusätzliche Round-Trip-Latenz bei einem entfernten Host).
  • MySQL sendet ein Handshake-Paket, der Client antwortet mit Benutzername/Passwort, und der Server authentifiziert dies.
  • Ist SSL/TLS aktiviert, findet zusätzlich ein Verschlüsselungs-Handshake statt.
  • Der Server weist einen Thread zu, um die Verbindung zu bedienen, und initialisiert die Sitzungsvariablen.

Für eine einzelne Verbindung mag das wenige Millisekunden dauern, aber auf einem Server, der Hunderte Anfragen pro Sekunde verarbeitet, vergrößert das Bezahlen dieser Kosten bei jeder Anfrage sowohl die Latenz als auch die Gefahr, schnell an MySQLs max_connections-Limit zu stoßen. Genau hier kommt der Verbindungspool ins Spiel: Er öffnet Verbindungen einmal und gibt sie nach Gebrauch nicht zurück, sondern legt sie in den Pool zurück, sodass die nächste Anfrage eine bereitstehende Verbindung wiederverwendet.

Was genau macht ein Connection Pool?

Ein Verbindungspool ist eine Komponente, die einen Satz vorab geöffneter, einsatzbereiter Datenbankverbindungen im Speicher hält. Fordert die Anwendung eine Verbindung an, verleiht der Pool eine ungenutzte; ist die Arbeit erledigt, wird die Verbindung nicht geschlossen, sondern an den Pool zurückgegeben. Typische Einstellungen sind:

  • Minimale Poolgröße: die geringste Anzahl an Verbindungen, die dauerhaft offen bleiben (vermeidet Cold-Start-Latenz).
  • Maximale Poolgröße: die größte gleichzeitig erlaubte Anzahl an Verbindungen; sie muss unter MySQLs max_connections bleiben.
  • Idle Timeout: wie lange eine ungenutzte Verbindung gehalten wird, bevor sie geschlossen wird.
  • Verbindungsvalidierung: prüfen, ob eine Verbindung noch lebt, bevor sie ausgegeben wird (meist ein einfacher Ping oder SELECT 1).

Das Ergebnis: Die Handshake-Kosten fallen über die Lebensdauer der Anwendung nur so oft an wie die Poolgröße, nicht für Tausende Anfragen. Das senkt die Latenz pro Anfrage und beseitigt nahezu die Last des Thread-Erstellens/-Zerstörens auf dem MySQL-Server.

PHP / Laravel: persistente Verbindungen

PHPs klassisches Modell "jede Anfrage beginnt von vorn" erschwert einen herkömmlichen Pool, weil der Prozess am Ende der Anfrage stirbt. Die Lösung sind persistente Verbindungen, die die Verbindung am Leben halten, solange die PHP-FPM-Prozesse leben. Mit PDO:

$pdo = new PDO($dsn, $user, $pass, [
    PDO::ATTR_PERSISTENT => true,
    PDO::ATTR_ERRMODE    => PDO::ERRMODE_EXCEPTION,
]);

In Laravel machst du dasselbe in config/database.php:

'mysql' => [
    'driver'  => 'mysql',
    // ...
    'options' => [
        PDO::ATTR_PERSISTENT => true,
    ],
],

Eine Warnung: Persistente Verbindungen halten für jeden PHP-FPM-Worker eine eigene Verbindung. Ist deine Worker-Zahl hoch, kann die Gesamtzahl der Verbindungen MySQLs Limit überschreiten. Plane Worker-Zahl und max_connections gemeinsam. Willst du einen echten Pool (Größensteuerung, Health-Checks), ist es robuster, ProxySQL vor deine PHP-Prozesse zu setzen.

Node.js: ein echtes Pool-Beispiel

Da Node single-process und langlebig ist, passt ein Verbindungspool hier natürlich. Mit der mysql2-Bibliothek:

const mysql = require('mysql2/promise');

const pool = mysql.createPool({
  host: '127.0.0.1',
  user: 'game',
  password: process.env.DB_PASS,
  database: 'server',
  connectionLimit: 20,
  waitForConnections: true,
  queueLimit: 0,
});

const [rows] = await pool.query(
  'SELECT level FROM players WHERE id = ?', [playerId]
);

pool.query() entnimmt bei jedem Aufruf eine Verbindung aus dem Pool, führt die Abfrage aus und gibt die Verbindung automatisch zurück. Für Operationen, die dieselbe Verbindung benötigen, etwa Transaktionen, holst du eine manuell mit pool.getConnection() und gibst sie am Ende mit connection.release() zurück — das Vergessen der Freigabe ist der häufigste Fehler, der einen Pool erschöpft.

Die Gameserver-Seite (C++)

Auf einem Metin2-basierten oder selbst geschriebenen C++-Gameserver gibt es meist kein fertiges ORM; du verwaltest den Pool selbst. Die Logik ist dieselbe: Beim Start öffnest du N Verbindungen und legst sie in eine thread-sichere Warteschlange; muss ein Thread eine Abfrage ausführen, zieht er eine Verbindung aus der Warteschlange und legt sie nach Abschluss zurück. Wichtige Punkte:

  • Thread-Sicherheit: schütze den Zugriff auf den Pool mit einem Mutex oder einer lock-freien Warteschlange. Zwei Threads können nicht gleichzeitig dieselbe MySQL-Verbindung nutzen.
  • Wiederverbinden: läuft wait_timeout ab, schließt MySQL die ungenutzte Verbindung und du erhältst einen "MySQL server has gone away"-Fehler. Prüfe mit mysql_ping(), bevor du sie ausgibst, und verbinde neu, wenn sie tot ist.
  • Dimensionierung: stimme den Pool auf die Anzahl der DB-Threads im Spielkern ab; mehr Verbindungen als nötig verschwenden nur MySQL-Speicher.

Den Pool richtig dimensionieren

Der häufigste Fehler ist die Annahme, "mehr Verbindungen sind immer besser". Das Gegenteil ist der Fall: zu viele gleichzeitige Verbindungen verlangsamen die Dinge, indem sie Kontextwechsel und Lock-Contention in MySQL erhöhen. Ein praktischer Ausgangspunkt ist ein relativ kleiner Pool, basierend auf der CPU-Kernzahl und der Disk-Parallelität (für die meisten Anwendungen reichen einige Verbindungen pro Kern). Überprüfe durch Messen:

SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Threads_running';
SHOW STATUS LIKE 'Max_used_connections';

Ist Threads_running dauerhaft hoch, liegt der Engpass nicht am Pool, sondern an deinen Abfragen; behebe zuerst langsame Abfragen und fehlende Indizes. Stößt Max_used_connections an max_connections, senke entweder deine Pool-Limits oder du brauchst tatsächlich mehr Kapazität.

Häufige Fragen

Hat MySQL einen eingebauten Connection Pool?

Nein, der klassische Verbindungspool ist ein clientseitiges Konzept. Auf Serverseite bieten MySQL Enterprise und MariaDB ein "Thread Pool"-Plugin; es bedient Verbindungen mit einer kleinen Anzahl Threads, beseitigt aber nicht die Kosten des Verbindungsaufbaus. Beide lösen unterschiedliche Probleme: Der Thread Pool bekämpft die Thread-Explosion auf dem Server, der Connection Pool die Aufbaukosten auf dem Client.

Ist eine persistente Verbindung dasselbe wie ein Connection Pool?

Nicht ganz. Eine persistente Verbindung schließt eine Verbindung nach einer Anfrage nicht und verwendet sie wieder, aber die Größen- und Zustandsverwaltung ist begrenzt. Ein echter Pool bietet Richtlinien wie minimale/maximale Größe, Idle Timeout, Health-Checks und Warteschlangenbildung. In PHP nutzt man für einen robusteren Pool eine Middleware wie ProxySQL.

Wann brauche ich ProxySQL?

ProxySQL ist sinnvoll, wenn du mehrere Anwendungsserver hast und die Gesamtzahl der Verbindungen zentral begrenzen, Abfragen routen oder Lese- und Schreibvorgänge trennen willst. Für kleine Single-Server-Projekte reicht ein Pool innerhalb der Anwendung meist aus.

Ersticken deine Datenbankverbindungen deinen Server? Wenn du Hilfe beim Einrichten und Dimensionieren eines Connection Pools oder beim MySQL-Tuning für deinen Gameserver oder dein Webprojekt brauchst, nimm Kontakt mit mir auf; ich prüfe deine aktuelle Konfiguration und erstelle einen konkreten Verbesserungsplan.

Bu kategorideki tüm yazılar →

Devamı için