Cloud SQL for MySQL der zweiten Generation als mobile Spiele-Back-End-Datenbank nutzen

Last reviewed 2022-10-28 UTC

Ein sorgfältig getestetes Muster zum Erstellen eines Online-Spiele-Back-Ends verwendet eine relationale Datenbank, z. B. MySQL. Diese Datenbank speichert Zustandsdaten der Spielewelt und essenzielle persistente Daten. Bei einfachen sitzungsbasierten Spielen enthält die Datenbank nichts, das komplizierter ist als die Endergebnisse der Spiele. Bei großen Massen-Online-Gemeinschaftsspielen (Massively Multiplayer Online, MMO) mit persistenter Spielwelt kann es sich um enorm komplizierte miteinander verknüpfte Tabellen handeln, in denen Spielerentwicklung und das Inventar festgehalten sind. Die Geschwindigkeit von Abfragen in der Datenbankschicht Ihres Back-Ends wirkt sich direkt auf die Nutzererfahrung in Bezug auf die Reaktionsfähigkeit im Spieleclient aus.

Obwohl dieses Muster bekannt ist, haben die meisten Spieleentwickler keinen speziellen Datenbankadministrator. Mit zunehmender Größe der Datenbank und zunehmender Komplexität der modellierten Beziehungen kann die Verwaltung zu einer Aufgabe werden, die von den meisten Teams lieber delegiert wird. Bei kleinen bis mittelgroßen, asynchronen, rundenbasierten mobilen Spielen ist eine Datenbank wie Google Cloud SQL gegebenenfalls eine hervorragende Wahl. Cloud SQL for MySQL der zweiten Generation bietet eine vollständig gehostete und verwaltete Instanz von MySQL mit solider Leistung, minimalen Betriebsvorgängen und automatisierten Backups.

Design eines serviceorientierten Datenbankmusters

Überblick über die Datenbankarchitektur

Das Mikrodienstparadigma eignet sich für mobile Spieledatenbank-Back-Ends. Bei einem häufig verwendeten Design liegt vor der Datenbank ein Datenbankdienst, der durch einen Pool von Worker-Prozessen gebildet wird und Anfragen vom Spiele-Frontend akzeptiert, diese für die Datenbank ausführt und die Ergebnisse zurückgibt.

Vorteile eines serviceorientierten Datenbankmusters

Es bringt einige Vorteile mit sich, wenn Sie für Ihre Spieleserver eine zwischengeschaltete Serviceabfrage in der Datenbank haben:

  • Erhöhte Verfügbarkeit: Datenbanken begrenzen oft die Anzahl gleichzeitiger Verbindungen. Ein Service entkoppelt die Anzahl der Spieleserver, die von den maximal erlaubten Verbindungen Anfragen an die Datenbank stellen können.
  • Fehlertoleranz: Datenbankdienste können entsprechend erstellt werden, um vorläufig Anfragen zu verarbeiten, wenn es bei der Datenbank Probleme gibt.
  • Optimierte Anfragen: Ein Datenbankdienst kann Datenbankanfragen optimieren und Folgendes bereitstellen:
    • Abfragebewertung
    • Abfragepriorisierung
    • Abfragegeschwindigkeitsregelung
    • Lese-Caching im Speicher
  • Datenbankabstraktion: Solange der Servicevertrag eingehalten wird, können der Datenbankdienst und die ihn stützende Datenbank ohne Veränderungen an den Frontend-Spieleservern ausgetauscht werden. Dieses Design ermöglicht Ihnen die Nutzung verschiedener Datenbanken in Entwicklungs- oder QA-Umgebungen oder das Migrieren in eine andere sich in der Produktion befindlichen Datenbanktechnologie.

Ein serviceorientiertes Datenbankmuster für Spiele

Die folgende Grafik veranschaulicht, wie Sie mit Google Cloud Platform-Diensten ein robustes serviceorientiertes Datenbankmuster erstellen können.

Google Cloud Platform nutzendes Datenbankmuster.

Ein robustes serviceorientiertes Datenbankmuster kann mit den folgenden Komponenten erstellt werden:

  • Zugeordnete Spieleserver/Frontend: Spieleclient-Anwendungen werden direkt mit den Spieleservern verbunden und sind daher ein Frontend-Dienst. Die zugeordneten Spieleserver werden normalerweise benutzerdefiniert ausgeführt und mit der Spiele-Engine, die auf einer virtualisierten Hardware wie Google Compute Engine VMs ausgeführt werden muss, erstellt. Wenn Sie ein Spiel schreiben, bei dem die Online-Interaktion mit einer Anfrage-/Antwort-Semantik im HTTP-Stil modelliert werden kann, ist möglicherweise auch Google App Engine eine gute Lösung.

  • Spieleserver-Kommunikation mit Datenbankdiensten: Diese wird oft mit der Zugriffsmethode CRUD (Create, Read, Update und Delete) modelliert. Hierfür eignet sich ein REST API- oder gRPC-Endpunkt in Compute Engine.

  • API/RPC-Endpunkt-Kommunikation mit einem Datenbank-Worker-Pool: Bei einer klassischen Warteschlange sind selbstverwaltete Open-Source-Warteschlangen-Middlewares wie RabbitMQ oder ZeroMQ eine beliebte Option. Auf der Cloud Platform können Sie den sicheren, robusten und hochverfügbaren Dienst Google Cloud Pub/Sub nutzen. Dieser bietet eine verwaltete Warteschlangenlösung, ohne dass die Server verwaltet werden müssen.

  • Mit Cloud SQL der zweiten Generation verbundene Datenbank-Worker: Datenbank-Worker können in jeder Sprache geschrieben werden, die eine aktuelle MySQL-Zugriffsmethode bietet. Diese Worker können manuell in Compute Engine verwaltet oder in Docker-Containern verpackt werden, um die Verwaltung mithilfe von Kubernetes DSL in Google Kubernetes Engine zu vereinfachen.

Bei der Nutzung von Cloud SQL der zweiten Generation sind einige Einschränkungen zu berücksichtigen:

  • Datenbankgröße von höchstens 10 TB
  • Verbindungsgrenze von 4.000 gleichzeitigen Verbindungen
  • Mangel an NDB-Support (Sharding), obwohl Repliken unterstützt werden

So können Sie diese Probleme angehen:

  • Wählen Sie ein Datenmodell, das die gespeicherte Datenmenge optimiert.

  • Integrieren Sie Prozesse, die kaum genutzte Daten aus der Hauptdatenbank entfernen und in ein Data Warehouse wie etwa Google BigQuery verschieben.

  • Verwenden Sie ein Muster, bei dem Spieleserver über einen Mikrodienst auf die Datenbank zugreifen. Selbst wenn die Anzahl der Spieler gering genug ist, dass Ihre Spieleserver direkt auf die Datenbank zugreifen könnten, hat das Entkoppeln der Datenbankschicht vom Spieleserver viele Vorteile: Warteschlangen, höhere Abfragegeschwindigkeit, Verbindungsfehlertoleranz usw. Außerdem kann der Versuch, eine separate Datenbankzugangsschicht hinzuzufügen, wenn Ihr Spiel schon beliebt ist, Ausfälle und Einnahmeeinbußen zur Folge haben.

  • Führen Sie eine Spieleanalyse und Spielertelemetrie für die schreibgeschützte Datenbankreplik aus. Dies verhindert, dass sich Ihre Analyse auf die Reaktionsfähigkeit der Datenbank auswirkt. Cloud SQL for MySQL der zweiten Generation erlaubt eine standardmäßige MySQL-Replikation. Sie können außerdem die Größe Ihrer zweiten Instanz für Analyseanfragen entsprechend anpassen, um die Kosten gering zu halten.

Beispieldesign: MASS-Spiel (Massively Single-Player Social)

Ein in den letzten zehn Jahre entstandenes Spielparadigma besteht aus einer enormen Anzahl an Einzelspielern, die mithilfe sozialer Mechanismen wie dem Verleihen und Handeln von Einheiten Sessions simultan online spielen. Bestenlisten dienen als einzige Kontaktpunkte zwischen den Spielern. Beispiele für MASS-Spiele sind Puzzle and Dragons™ sowie Monster Strike™. Mobile MASS-Spiele sind für eine effiziente Client/Server-Kommunikation entwickelt. Dies ermöglicht es den Nutzern, ihr Spiel auch bei einer eingeschränkten oder unregelmäßigen Internetverbindung zu genießen. Das Datenmodell für ein Spiel wie dieses, bei dem fast der gesamte nichtflüchtige Zustandsspeicher zum Meta-Spiel (Sammeln von Einheiten und Pflegen der Spielwährung) gehört, verfügt über zwei grundlegende Arten von Objekten, die in der Datenbank zu speichern sind. Diese Objekte können mithilfe von CRUD-Mechanismen leicht verändert werden.

Spielerobjekte, die Folgendes nachverfolgen:

  • Spielwährungen und echte Währungen
  • Gesamtmenge der Einheitenbestand-Slots
  • Ausdauer
  • Erfahrung

Einheitenobjekte, die Folgendes nachverfolgen:

  • Inhaber (Spieler-ID)
  • Erfahrung
  • Kosten für den Erwerb
  • Einheitenbestand

Bei weniger als 100.000 Spielern passt dieses Datenmodell gut in eine relationale Datenbank wie Cloud SQL Second Generation.

Mimus

Mimus ist eine simulierte, mobile MASS-Spieleanwendung mit einem Backend im Stil von Puzzle and Dragons™ oder Monster Strike™. Sie geht davon aus, dass jeder Spieler nur mit einem Gerät gleichzeitig angemeldet sein kann und dass jede Aktion erst abgeschlossen werden muss, bevor eine andere begonnen werden kann. Mit Mimus können Sie simulierte Arbeitslasten ausführen, um die optimale Kapazität der Architektur zu bewerten, die typischerweise auf der Anzahl der gleichzeitigen Nutzer (CCU) beruht.

Den Mimus-Quellcode finden Sie unter https://github.com/GoogleCloudPlatform/mimus-game-simulator.

Überblick über die Mimus-Architektur

Mimus-Client-Simulation innerhalb des Mimus-Servers

Bei MASS-Spielen kann die Geschwindigkeit, mit der der Spieleclient Datenbankabfragen generiert, vom Spieleentwickler gesteuert werden. Dazu muss der Spieler Animationen ansehen und zum Fortfahren mit dem Spieleclient interagieren. Mimus simuliert diese geschwindigkeitsbegrenzenden Strategien mit sleep()-Anrufen. Auf diese Weise simuliert Mimus eine vorsichtige Annäherung der Datenbanklast pro Client durch das Ausführen von der gleichen Anzahl an Prozessen wie simulierten Spielern. Dies wird mit einem Mimus-Client/Server-Pod in einem Container und in einem Kubernetes-Cluster, der Abfragen für die Datenbank generiert, effizient orchestriert.

Der Mimus-Spieleclient simuliert mit einer Dauerschleife, welche die Mimus-Server-Vorgänge direkt anspricht, eine Kommunikation mit dem Backend-Server und wählt aufzurufende Funktionen basierend auf dem Status und Inventar des Spielers.

Die von Mimus simulierten Spieleraktionen umfassen:

  • Eine Runde des Spiels spielen
  • Währung kaufen
  • Währung ausgeben
  • Einheiten aufleveln oder weiterentwickeln

All diese Aktionen werden als multiple CRUD-Interaktionen mit den Spieler- oder Einheitenobjekten implementiert und vom Client über Aufrufe des Mimus-Servers verändert. Der Mimus-Server nimmt diese Datenbankabfragen mit der synchronen (blockierenden) Mimus-Datenbank-API vor. Bei dieser API handelt es sich um ein vom Mimus-Server importiertes Python-Modul. Sie kann entsprechend konfiguriert werden, um verschiedene Datenbank-Backend-Implementierungen zu testen.

Kommunikation der Mimus-Datenbank-API mit dem Mimus-Datenbank-Worker-Pool

Die folgende Grafik stellt die Kommunikation zwischen dem Mimus-Server und dem Datenbankdienst dar.

Kommunikationsdesign von Mimus.

Die Mimus-Datenbank-API akzeptiert Teile von Datenbankabfragen und gibt die Ergebnisse zurück. Sie veröffentlicht diese Teile als Cloud Pub/Sub-Nachrichten und wartet darauf, dass die Ergebnisse über Redis zurückgegeben werden. Vor dem Senden einer Nachricht prüft die Datenbank-API alle Werte, die in die Datenbank geschrieben werden, und markiert die Nachrichten mit einer eindeutigen Transaktions-ID. Die Nachricht wird dann in einem Work-Thema in Cloud Pub/Sub veröffentlicht. Anschließend führt die Datenbank-API eine Schleife aus und prüft die Existenz der Transaktions-ID als Redis-Schlüssel. Die Ergebnisse im Wert des Schlüssels werden dann von der Datenbank API abgerufen und an den Mimus-Server zurückgegeben, von dem aus sie für den Mimus-Client verfügbar gemacht werden.

Berücksichtigungen bei der Wahl der Kommunikationsart

Mimus verwendet Cloud Pub/Sub für die ausstehende Abfrage-Kommunikation, da Nutzeraktionen eine dauerhafte und zuverlässige Bereitstellung erfordern. Mimus nutzt Redis für die Kommunikation der Ergebnisse, bei denen die Dauerhaftigkeit und Zuverlässigkeit weniger kritisch ist. Wenn die Ergebnisse aufgrund eines Anwendungs- oder Redis-Fehlers verloren gehen, fragt der Mimus-Client die Endergebnisse aus der Datenbank noch einmal ab. Mit Transaktionen in einer relationalen Datenbank haben Sie die Garantie, dass alle oder keine der angefragten Änderungen stattgefunden haben. Der seltene Fall, dass Mimus eine zweite Anfrage stellen muss, wird als akzeptabler Kompromiss für die leichte und schnelle Abfrage von Redis angesehen. Um die Nutzung von Redis in Grenzen zu halten, dürfen die Abfrageergebnisse von Redis nach 30 Sekunden verfallen.

Mimus-Datenbank-Worker-Pool

Der Mimus-Datenbank-Worker-Pool enthält mehrere Prozesse, die auf Kubernetes Engine ausgeführt werden. Jede ausgeführte Container-Instanz fragt das Cloud Pub/Sub-Thema Work in einer Endlosschleife auf neue Nachrichten ab. Wenn sie eine Nachricht empfängt, führt sie mithilfe des Python MySQLdb-Moduls die Abfragen in der Nachricht aus. Eine einzelne Nachricht repräsentiert eine relationale Transaktion und kann mehrere Abfragen enthalten. Alle Abfragen in der Nachricht müssen abgeschlossen sein, bevor sie auf die Datenbank angewendet werden. Sobald die Abfragen abgeschlossen (oder fehlgeschlagen) sind, veröffentlicht der Datenbank-Worker die Ergebnisse bei Redis unter der Transaktions-ID, die er als Teil der ursprünglichen Nachricht empfangen hat.

Mimus-Datenbank

Die relationale Datenbank, die den Mimus-Datenbankdienst stützt, ist eine Cloud SQL for MySQL-Instanz der zweiten Generation. Beim Erstellen der Instanz können Sie Maschinenkonfigurationen von bis zu 32 Kernen und 208 GB RAM mit Festplattengrößen von bis zu 10 TB auswählen. Zusätzlich zum Support für Replikate werden Cloud SQL-Instanzen der zweiten Generation für das standardmäßige Ausführen regulärer Sicherungen konfiguriert. Wenn Sie für MySQL zusätzliche Funktionen benötigen, können Sie bestimmte Cloud SQL-Flags auf der Cloud SQL-Instanz festlegen. Informationen zur Konfiguration finden Sie in der Cloud SQL-Dokumentation.

Zusammengefasste Ergebnisse für Cloud SQL-Tests mit Mimus

Maschinentyp Cloud SQL SG Vorgeschlagene Anzahl der gleichzeitigen Nutzer
n1-standard-4 15.000
n1-standard-8 30.000
n1-standard-16 60.000
n1-standard-32 120.000
n1-highmem-8 50.000
n1-highmem-16 100.000
n1-highmem-32 200.000

Wenn Sie den Mimus-Test nutzen, um 100.000 gleichzeitige Nutzer für eine Cloud SQL for MySQL-Instanz der zweiten Generation zu simulieren, die auf einer n1-highmem-16 Compute Engine-Instanz erstellt wurde, blieb die Reaktionszeit der Abfrage beim gesamten Test unter 2 Sekunden.

Wenn Sie ein mobiles MASS-Spiel erstellen und Hunderttausende gleichzeitige Spieler unterstützen müssen, kann ein serviceorientiertes Datenbankmuster basierend auf Cloud SQL for MySQL der zweiten Generation die benötigte Leistung liefern. Wenn Ihr Spiel beliebter wird, können Sie zusätzliche Cloud SQL-Instanzen – entweder fragmentiert oder als Repliken – sowie eine Redis- oder Memcached-Caching-Strategie auf Ebene des Datenbankdienstes hinzufügen, um die Leistung akzeptabel zu halten.

Bei Spielen mit weniger projizierten gleichzeitigen Nutzern können Sie einen kleineren Maschinentyp einsetzen, um die Kosten zu reduzieren.

Bei Spielen mit Millionen von projizierten gleichzeitigen Nutzern, empfiehlt es sich, eine NoSQL-Datenbank wie etwa Google Cloud Datastore oder Google Cloud Bigtable mit starken Skalierbarkeits- und Leistungseigenschaften in Betracht zu ziehen.

Nächste Schritte

  • Referenzarchitekturen, Diagramme und Best Practices zu Google Cloud kennenlernen. Weitere Informationen zu Cloud Architecture Center