Best Practices für die Verwendung von Cloud Spanner als Gaming-Datenbank

In diesem Dokument werden Best Practices für die Verwendung von Spanner als primäre Back-End-Datenbank zum Speichern von Spielstatus beschrieben. Spanner können Sie anstelle von gängigen Datenbanken einsetzen, um Daten zur Authentifizierung von Spielern und Inventardaten zu speichern. Dieses Dokument richtet sich an Entwickler von Spiele-Back-Ends, die an der langfristigen Speicherung von Status arbeiten. Außerdem wendet es sich an Betreiber von Spieleinfrastruktur und an Administratoren, die diese Systeme unterstützen und ihre Back-End-Datenbank in Google Cloud hosten möchten.

Für Spiele mit Mehrspielermodus und Onlinespiele sind immer komplexere Datenbankstrukturen erforderlich, um Spielerberechtigungen, Status und Inventardaten zu verfolgen. Die wachsende Spielerbasis und die zunehmende Komplexität der Spiele haben zu Datenbanklösungen geführt, deren Skalierung und Verwaltung eine Herausforderung darstellt und bei denen häufig eine Fragmentierung oder ein Clustering notwendig sind. Damit wertvolle In-Game-Elemente oder wichtige Spielerfortschritte verfolgt werden können, sind in der Regel Transaktionen erforderlich. Bei vielen Arten verteilter Datenbanken lässt sich das nur schwer umgehen.

Spanner ist der erste global verteilte Datenbankdienst mit strikter Konsistenz für Unternehmen, der für die Cloud entwickelt wurde und die Vorteile einer relationalen Datenbankstruktur mit nicht relationaler horizontaler Skalierung vereint. Zahlreiche Spieleunternehmen setzen den Dienst bereits ein, da er sich gut eignet, um in Produktionsumgebungen Datenbanken für den Spielstatus und solche für die Authentifizierung zu ersetzen. Mit der Cloud Console können Sie zur Skalierung Knoten hinzufügen und so die Leistung oder den Speicherplatz erhöhen. Spanner kann die globale Replikation transparent mit strikter Konsistenz handhaben, sodass Sie regionale Replikate nicht mehr verwalten müssen.

Dieses Best Practices-Dokument behandelt folgende Themen:

  • Wichtige Konzepte von Spanner und Unterschiede zu Datenbanken, die häufig in Spielen verwendet werden
  • Wann Spanner die richtige Datenbank für Ihr Spiel ist
  • Muster, die Sie beim Einsatz von Spanner für Spiele vermeiden sollten
  • Gestaltung Ihrer Datenbankvorgänge mit Spanner als Datenbank für Ihr Spiel
  • Modellierung von Daten und Erstellung eines Schemas mit dem Ziel, mit Spanner die Leistung zu optimieren

Terminologie

Berechtigungen
Spiele, Erweiterungen oder In-App-Käufe eines Spielers.
Personenidentifizierbare Informationen (personally identifiable information, PII)
Bei Spielen gehören zu diesen Informationen in der Regel die E-Mail-Adresse und Angaben zum Zahlungskonto wie eine Kreditkartennummer und eine Rechnungsadresse. In einigen Märkten können diese Informationen auch eine nationale Ausweisnummer umfassen.
Spieledatenbank (Spiele-DB)
Eine Datenbank, die den Spielerfortschritt und das Inventar für ein Spiel enthält.
Authentifizierungsdatenbank (Auth-DB)
Eine Datenbank mit Spielerberechtigungen und den personenidentifizierbaren Informationen, die die Spieler beim Kauf verwenden. Die Authentifizierungsdatenbank wird auch als Konto- oder Spielerdatenbank bezeichnet. Diese Datenbank wird manchmal mit der Datenbank des Spiels kombiniert, bei Studios oder Publishern mit mehreren Titeln wird sie jedoch häufig getrennt.
Transaktion
Eine Datenbanktransaktion, d. h. ein Satz von Schreibvorgängen mit Alles-oder-Nichts-Effekt. Die Transaktion ist entweder erfolgreich und alle Aktualisierungen werden wirksam oder die Datenbank wird in einen Status zurückgesetzt, der keine Aktualisierung der Transaktion enthält. Bei Spielen sind Datenbanktransaktionen bei der Verarbeitung von Zahlungen und beim Zuweisen der Eigentümerschaft von In-Game-Inventar oder -Währung entscheidend.
Managementsystem für relationale Datenbanken (relational database management system, RDBMS)
Ein Datenbanksystem, das auf Tabellen und Zeilen basiert, die aufeinander verweisen. SQL Server, MySQL und (seltener) Oracle® sind Beispiele für relationale Datenbanken, die bei Spielen zum Einsatz kommen. Diese werden häufig verwendet, weil sie vertraute Methoden und zuverlässige Garantien für Transaktionen bieten können.
NoSQL-Datenbank (NoSQL-DB)
Datenbanken, die nicht relational strukturiert sind. Diese Datenbanken werden immer beliebter bei Spielen, da sie sehr flexibel sind, wenn sich das Datenmodell ändert. Zu den NoSQL-Datenbanken gehören MongoDB und Cassandra.
Primärschlüssel
Normalerweise die Spalte mit der eindeutigen ID für Inventar-Items, Spielerkonten und Kauftransaktionen.
Instanz
Eine einzelne Datenbank. Ein Cluster führt beispielsweise mehrere Kopien der Datenbanksoftware aus, wird aber im Spiele-Back-End als einzelne Instanz angezeigt.
Knoten
In diesem Dokument ein einzelner Computer, auf dem eine Kopie der Datenbanksoftware ausgeführt wird.
Replikat
Eine zweite Kopie einer Datenbank. Replikate werden häufig für die Datenwiederherstellung und Hochverfügbarkeit verwendet oder erhöhen den Lesedurchsatz.
Cluster
Mehrere Kopien der Software, die auf vielen Computern ausgeführt werden und die das Spiele-Back-End als eine einzelne Instanz betrachtet. Clustering dient der Skalierbarkeit und Verfügbarkeit.
Shard
Eine Instanz einer Datenbank. Viele Spielestudios führen mehrere homogene Datenbankinstanzen aus, von denen jede einen Teil der Spieldaten enthält. Eine einzelne solche Instanz wird als Shard bezeichnet. Sharding bzw. eine Fragmentierung erfolgt in der Regel aus Gründen der Leistung oder Skalierbarkeit. Dabei ergeben sich Effizienzeinbußen bei der Verwaltung und die Anwendung wird komplexer. Die Fragmentierung in Spanner wird mithilfe von Splits umgesetzt.
Split
Spanner teilt Ihre Daten in Portionen auf, die sogenannten Splits. Dabei können einzelne Splits unabhängig voneinander verschoben und verschiedenen Servern zugewiesen werden. Ein Split wird als eine Reihe von Zeilen in einer übergeordneten (mit anderen Worten: nicht verschränkten) Tabelle definiert, wobei die Zeilen nach Primärschlüssel geordnet sind. Die Start- und Endschlüssel dieses Bereichs werden als "Split-Grenzen" bezeichnet. Spanner fügt automatisch Split-Grenzen hinzu und entfernt sie, was die Anzahl der Splits in der Datenbank verändert. Spanner teilt Daten nach Last auf: Es fügt Split-Grenzen automatisch hinzu, wenn eine hohe Lese- oder Schreiblastverteilung über mehrere Schlüssel in einem Split erkannt wird.
Hotspot
Ein Hotspot ist ein einzelner Split in einer verteilten Datenbank wie Spanner, der über Datensätze verfügt, die einen großen Teil aller an die Datenbank gestellten Abfragen erhalten. Hotspot-Szenarien gilt es zu vermeiden, da sie die Leistung beeinträchtigen.

Einsatz von Spanner für Spiele

In den meisten Fällen, in denen ein RDBMS für ein Spiel zum Einsatz kommen soll, ist Spanner die richtige Wahl, denn der Dienst kann entweder die Spieledatenbank, die Authentifizierungsdatenbank oder in vielen Fällen beide ersetzen.

Spieledatenbanken

Spanner kann als eine einzige weltweite Transaktionsstelle fungieren und eignet sich daher hervorragend für Inventarsysteme von Spielen. In umfangreichen Spiele-Back-Ends stellt jede Währung und jedes Item, das gehandelt, verkauft, verschenkt oder anderweitig von einem Spieler auf einen anderen übertragen werden kann, eine Herausforderung dar. Wenn ein Spiel besonders populär ist, reicht die herkömmliche Datenbank, die alles auf einem einzigen Knoten verarbeiten kann, häufig nicht aus. Je nach Art des Spiels kann die Datenbank schon durch die Anzahl der Vorgänge, die für die Verarbeitung von Spielerlasten erforderlich sind, stark beansprucht sein. Aber auch die Menge der gespeicherten Daten kann ihr zu schaffen machen. Deshalb fragmentieren Spieleentwickler häufig ihre Datenbank für zusätzliche Leistung oder speichern ständig wachsende Tabellen. Diese Art von Lösung sorgt dann jedoch für einen komplexeren Betrieb und einen hohen Wartungsaufwand.

Eine verbreitete Strategie zur Vermeidung dieser Komplexität besteht darin, vollständig getrennte Spielregionen auszuführen. Dabei geht die Möglichkeit verloren, Daten zwischen ihnen zu verschieben. In diesem Fall können Items und Währungen also nicht zwischen Spielern in verschiedenen Spielregionen gehandelt werden, da das Inventar in jeder Region in separate Datenbanken aufgeteilt wird. Dieses Setup macht es dem Entwickler leichter und den Betrieb des Spiels einfacher, vernachlässigt aber das bevorzugte Spielererlebnis.

Andererseits können Sie den Handel über Regionen hinweg in einer geografisch fragmentierten Datenbank zulassen. Das ist jedoch häufig mit hoher Komplexität verbunden. Bei diesem Setup müssen Transaktionen mehrere Datenbankinstanzen durchlaufen, was auf der Anwendungsseite zu einer komplexen, fehleranfälligen Logik führt. Wenn Sie für mehrere Datenbanken Transaktionssperren erreichen möchten, kann sich das erheblich auf die Leistung auswirken. Sich nicht auf atomare Transaktionen verlassen zu können, kann darüber hinaus zu Spieler-Exploits wie der Duplizierung von Währung oder Items im Spiel sorgen, und das schadet der Umgebung und der Community des Spiels.

Spanner kann den Ansatz bei Transaktionen von Inventar und Währung vereinfachen. Auch wenn Spanner sämtliche Spieldaten weltweit speichert, bietet es Lese-Schreib-Transaktionen mit noch stärkeren Attributen als den herkömmlichen für Atomarität, Konsistenz, Isolation und Langlebigkeit (ACID). Dank der Skalierbarkeit von Spanner bedeutet dies, dass Daten nicht in separate Datenbankinstanzen aufgeteilt werden müssen, wenn mehr Leistung oder Speicher benötigt wird. Stattdessen fügen Sie einfach weitere Knoten hinzu. Darüber hinaus werden die hohe Verfügbarkeit und Robustheit der Daten, für die Datenbanken von Spielen häufig in Clustern zusammengefasst werden, von Cloud Spanner transparent gehandhabt, sodass keine zusätzliche Einrichtung oder Verwaltung erforderlich ist.

Authentifizierungsdatenbanken

Auch Authentifizierungsdatenbanken können gut von Spanner bereitgestellt werden. Das gilt insbesondere, wenn Sie auf Studio- oder Publisher-Ebene standardmäßig ein einzelnes RDBMS anbieten möchten. Obwohl Authentifizierungsdatenbanken für Spiele häufig nicht die Größe brauchen, die Spanner bietet, können die Transaktionsgarantien und die hohe Datenverfügbarkeit der Anwendung in bestimmten Fällen ein schlagendes Argument sein. Die Datenreplikation ist in Spanner integriert und erfolgt transparent und synchron. Der Dienst bietet Konfigurationen mit einer Verfügbarkeit von 99,99 % ("viermal die Neun") oder 99,999 % ("fünfmal die Neun"), wobei die "fünfmal die Neun"-Konfiguration weniger als fünfeinhalb Minuten Nichtverfügbarkeit in einem Jahr entspricht. Diese Art der Verfügbarkeit macht Cloud Spanner zu einer guten Wahl für den entscheidenden Authentifizierungspfad, den jeder Spieler zu Beginn einer Sitzung gehen muss.

Best Practices

Dieser Abschnitt enthält Empfehlungen zur Verwendung von Spanner im Spieledesign. Damit Sie von den einzigartigen Features von Spanner profitieren können, ist es wichtig, dass Sie die Spieldaten entsprechend modellieren. Zwar ist es möglich, mit der Semantik von relationalen Datenbanken auf Spanner zuzugreifen, einige Punkte im Schemadesign können aber zur Leistungssteigerung beitragen. Die Dokumentation zu Spanner enthält detaillierte Empfehlungen zum Schemadesign. In den folgenden Abschnitten finden Sie außerdem einige Best Practices für Spieledatenbanken.

Die Methoden in diesem Dokument basieren auf Erfahrungen aus Kundenanwendungen und Fallstudien.

UUIDs als Spieler- und Charakter-IDs verwenden

Die Spielertabelle enthält in der Regel eine Zeile für jeden Spieler sowie die Währung, den Fortschritt oder andere Daten im Spiel, die sich nicht leicht zu einzelnen Tabellenzeilen für Inventar zuordnen lassen. Wenn Spieler bei einem Spiel für mehrere Charaktere den Fortschritt separat speichern können, wie bei vielen großen Massive-Multiplayer-Games möglich, enthält diese Tabelle dann in der Regel eine Zeile für jeden Charakter. Ansonsten ist das Muster gleich.

Es empfiehlt sich, als Primärschlüssel der Charaktertabelle eine global eindeutige Charakter- oder Spielerkennung (Charakter-ID) zu verwenden. Außerdem sollten Sie die UUID (Universally Unique Identifier) in der Version 4 verwenden. Diese verteilt die Spielerdaten auf Datenbankknoten und trägt so dazu bei, dass Spanner eine höhere Leistung erbringen kann.

Verschränkung für Inventartabellen verwenden

Die Inventartabelle enthält häufig In-Game-Elemente wie Charakterausrüstung, Karten oder Einheiten. In der Regel hat ein einzelner Spieler zahlreiche Items in seinem Inventar. Jedes Item wird durch eine einzelne Zeile in der Tabelle dargestellt.

Ähnlich wie bei anderen relationalen Datenbanken hat eine Inventartabelle in Spanner einen Primärschlüssel, der eine global eindeutige Kennung für das Item ist, wie in der folgenden Tabelle dargestellt.

itemID type playerID
7c14887e-8d45 1 6f1ede3b-25e2
8ca83609-bb93 40 6f1ede3b-25e2
33fedada-3400 1 5fa0aa7d-16da
e4714487-075e 23 5fa0aa7d-16da
d4fbfb92-a8bd 14 5fa0aa7d-16da
31b7067b-42ec 3 26a38c2c-123a

In der Inventartabelle aus dem Beispiel sind itemID und playerID zur besseren Lesbarkeit abgeschnitten. Eine echte Inventartabelle hätte noch viele andere Spalten, die im Beispiel nicht enthalten sind.

Wenn es darum geht, die Eigentümerschaft der Items zu verfolgen, wird in einem RDBMS üblicherweise eine Spalte als Fremdschlüssel verwendet, die die Spieler-ID des aktuellen Eigentümers enthält. Diese Spalte ist der Primärschlüssel einer separaten Datenbanktabelle. In Spanner können Sie Verschränkungen verwenden. Dabei werden die Inventarzeilen in der Nähe der zugehörigen Zeile in der Spielertabelle gespeichert, um die Leistung zu verbessern. Wenn Sie verschränkte Tabellen verwenden, sollten Sie Folgendes beachten:

  • Die Größe der Gesamtdaten in der Spielerzeile und allen untergeordneten Inventarzeilen muss unter ~4 GiB bleiben. Bei einem entsprechenden Design des Datenmodells stellt diese Beschränkung normalerweise kein Problem dar.
  • Ohne Eigentümer können Sie kein Objekt erstellen. Eigentümerlose Objekte lassen sich im Spieledesign vermeiden, wenn die Beschränkung im Voraus bekannt ist.

Indexierung gestalten, um Hotspots zu vermeiden

Viele Spieleentwickler implementieren Indexe für viele der Inventarfelder, um bestimmte Abfragen zu optimieren. In Spanner erzeugt das Erstellen oder Aktualisieren einer Zeile mit Daten in diesem Index eine zusätzliche Schreiblast, die der Anzahl der indexierten Spalten entspricht. Hier können Sie die Leistung von Spanner verbessern: Entfernen Sie dafür eher selten verwendete Indexe oder implementieren Sie diese Indexe auf andere Weise, sodass sie sich nicht auf die Datenbankleistung auswirken.

Im folgenden Beispiel gibt es eine Tabelle mit Highscore-Einträgen von Langzeitspielern:

CREATE TABLE Ranking (
        PlayerID STRING(36) NOT NULL,
        GameMode INT64 NOT NULL,
        Score INT64 NOT NULL
) PRIMARY KEY (PlayerID, GameMode)

Diese Tabelle enthält die Spieler-ID (UUIDv4), eine Zahl, die einen Spielmodus, eine Phase oder eine Staffel sowie die Punktzahl des Spielers darstellt.

Zum Beschleunigen von Abfragen, die nach dem Spielmodus filtern, setzen Sie den folgenden Index ein:

CREATE INDEX idx_score_ranking ON Ranking (
        GameMode,
        Score DESC
)

Wenn jeder den gleichen Spielmodus, nämlich 1, spielt, entsteht bei diesem Index ein Hotspot bei GameMode=1. Wenn Sie für diesen Spielmodus eine Rangfolge abrufen möchten, durchsucht der Index nur die Zeilen mit GameMode=1 und gibt die Rangfolge schnell zurück.

Dieses Problem können Sie lösen, wenn Sie die Reihenfolge des vorherigen Index ändern:

CREATE INDEX idx_score_ranking ON Ranking (
        Score DESC,
        GameMode
)

Bei diesem Index entsteht durch Spieler, die im gleichen Spielmodus gegeneinander spielen, kein bedeutender Hotspot. Voraussetzung dafür ist aber, dass ihre Punktzahlen über den möglichen Bereich verteilt sind. Das Abrufen von Punktzahlen erfolgt dann jedoch nicht so schnell wie beim vorherigen Index, da die Abfrage alle Punktzahlen aus allen Modi scannt, um zu ermitteln, ob GameMode=1 ist.

Der Index mit neuer Reihenfolge löst also das Hotspot-Problem beim Spielmodus. Trotzdem besteht Verbesserungsbedarf, wie im folgenden Design veranschaulicht:

CREATE TABLE GameMode1Ranking (
        PlayerID STRING(36) NOT NULL,
        Score INT64 NOT NULL
) PRIMARY KEY (PlayerID)

CREATE INDEX idx_score_ranking ON Ranking (
        Score DESC
)

Sie sollten den Spielmodus aus dem Tabellenschema herausnehmen und falls möglich pro Modus eine Tabelle verwenden. Wenn Sie mit dieser Methode die Punktzahlen für einen Modus abrufen, wird nur eine Tabelle mit Punktzahlen für diesen Modus abgefragt. Diese Tabelle kann nach Punktzahl indexiert werden und Punktzahlbereiche lassen sich auf diese Weise schnell abrufen, ohne dass Hotspots entstehen (vorausgesetzt, die Punktzahlen sind gut verteilt). Als dieses Dokument erstellt wurde, betrug die maximale Anzahl von Tabellen pro Datenbank in Spanner 2.560. Das ist für die meisten Spiele mehr als ausreichend.

Separate Datenbanken pro Mandant

Im Gegensatz zu anderen Arbeitslasten, bei denen wir ein Design für die Mehrinstanzfähigkeit in Spanner empfehlen, das mithilfe von verschiedenen Primärschlüsselwerten umgesetzt werden kann, raten wir bei Spieldaten zu dem konventionelleren Ansatz mit einzelnen Datenbanken pro Mandant. Bei der Veröffentlichung neuer Features in Echtzeit-Spielen gibt es häufig Schemaänderungen. Wenn Mandanten auf Datenbankebene isoliert werden, kann dies Schemaaktualisierungen vereinfachen. Mit dieser Strategie lässt sich auch die Zeit für die Sicherung oder Wiederherstellung der Daten eines Mandanten optimieren, da diese Vorgänge für eine gesamte Datenbank gleichzeitig ausgeführt werden.

Inkrementelle Schemaaktualisierungen vermeiden

Im Gegensatz zu manchen konventionellen relationalen Datenbanken bleibt Spanner während der Schemaaktualisierung funktionsfähig. Alle Abfragen für das alte Schema werden zurückgegeben, wobei dies etwas länger als üblich dauern kann. Abfragen für das neue Schema werden zurückgegeben, sobald sie verfügbar sind. Sie können den Aktualisierungsvorgang so gestalten, dass das Spiel, wenn es auf Spanner läuft, während der Schemaaktualisierung weiter ausgeführt wird – wenn Sie dabei die oben genannten Einschränkungen beachten.

Wenn Sie jedoch eine Anfrage für eine weitere Schemaänderung stellen, während eine andere gerade verarbeitet wird, wird die neue Aktualisierung in die Warteschlange gestellt und erst nach Abschluss aller vorherigen Schemaaktualisierungen ausgeführt. Dies können Sie vermeiden, wenn Sie größere Schemaaktualisierungen planen, statt in kurzer Zeit viele inkrementelle Schemaaktualisierungen durchzuführen. Weitere Informationen zu Schemaaktualisierungen, einschließlich der Ausführung von Schemaaktualisierungen mit erforderlicher Datenvalidierung, finden Sie in der Dokumentation zu Spanner-Schemaaktualisierungen.

Datenbankzugriff und -größe berücksichtigen

Wenn Sie einen Spieleserver und Plattformdienste für die Verwendung von Spanner entwickeln, achten Sie darauf, wie das Spiel auf die Datenbank zugreift und welche Größe die Datenbank haben sollte, um unnötige Kosten zu vermeiden.

Native Treiber und Bibliotheken verwenden

Berücksichtigen Sie bei der Entwicklung für Spanner, wie der Code mit der Datenbank interagiert. Spanner bietet native Clientbibliotheken für zahlreiche gängige Sprachen, die in der Regel eine Menge Features und Leistung haben. Darüber hinaus gibt es JDBC-Treiber, die Anweisungen in Datenbearbeitungssprache (Data Manipulation Language, DML) und Datendefinitionssprache (Data Definition Language, DDL) unterstützen. Wenn Spanner im Rahmen von Neuentwicklungen verwendet wird, empfiehlt sich der Einsatz der Cloud-Clientbibliotheken für Spanner. Typische Integrationen von Spiel-Engines sind bei der Sprachauswahl zwar nicht sehr flexibel, es gibt bei Plattformdiensten, die auf Spanner zugreifen, aber Gaming-Kunden, die Java oder Go verwenden. Bei Anwendungen mit hohem Durchsatz sollten Sie daher eine Bibliothek auswählen, in der Sie denselben Spanner-Client für mehrere sequenzielle Anfragen verwenden können.

Größe der Datenbank auf Test- und Produktionsanforderungen abstimmen

Während der Entwicklung reicht wahrscheinlich eine Spanner-Instanz mit einem Knoten für die meisten Aktivitäten, auch Funktionstests, aus.

Anforderungen an Spanner für die Produktion evaluieren

Wenn Sie nach der Entwicklung zur Testphase und anschließend zur Produktion übergehen, ist eine neue Evaluierung Ihrer Anforderungen an Spanner wichtig, damit Ihr Spiel die Live-Spielerzahlen bewältigen kann.

Bevor Sie zur Produktion übergehen, sollten Sie unbedingt Lasttests durchführen, um zu überprüfen, ob Ihr Back-End die Last während der Produktion verarbeiten kann. Dabei sollten Sie Lasttests mit der doppelten Last durchführen, die Sie in der Produktion erwarten. So sind Sie auf Auslastungsspitzen ebenso wie auf den Fall vorbereitet, dass Ihr Spiel bei der Beliebtheit Ihre Erwartungen übertrifft.

Lasttests mit realen Daten ausführen

Ein Lasttest mit synthetischen Daten ist nicht ausreichend. Sie sollten außerdem Lasttests mit Daten und Zugriffsmustern durchführen, die sich möglichst nahe an den Erwartungen in der Produktion bewegen. Mit synthetischen Daten sind möglicherweise auch potenzielle Hotspots im Schemadesign für Spanner nicht zu erkennen. Am besten ist ein (offener oder geschlossener) Betatest mit echten Spielern. Denn so können Sie prüfen, wie sich Spanner bei echten Daten verhält.

Das folgende Diagramm ist ein Beispiel für ein Spielertabellenschema eines Spielestudios, das beispielhaft zeigt, wie wichtig Betatests für das Testen von Lasten sind.

Liste der Spielernamen und ein Attribut für Lasttests.

Das Spielestudio hat anhand von Trends eines vorherigen Spiels, das es einige Jahre lang betrieben hatte, diese Daten vorbereitet. Das Unternehmen ging davon aus, dass die Daten mit diesem Schema im neuen Spiel gut dargestellt würden.

Mit jedem Spielerdatensatz sind einige numerische Attribute verknüpft, die den Fortschritt des Spielers im Spiel darstellen (z. B. Rang und Spielzeit). Für das in der vorhergehenden Tabelle verwendete Beispielattribut erhalten neue Spieler einen Startwert von 50. Dieser Wert ändert sich dann im Laufe des Spiels auf einen Wert zwischen 1 und 100.

Das Studio möchte für dieses Attribut einen Index erstellen, um wichtige Abfragen während des Spiels zu beschleunigen.

Auf Grundlage dieser Daten erstellte das Studio die folgende Spanner-Tabelle mit einem Primärschlüssel. Dafür wurden die PlayerID und ein sekundärer Index für Attribute verwendet.

CREATE TABLE Player (
        PlayerID STRING(36) NOT NULL,
        Attribute INT64 NOT NULL
) PRIMARY KEY (PlayerID)

CREATE INDEX idx_attribute ON Player(Attribute)

Der Index wurde wie folgt abgefragt, um bis zu zehn Spieler mit Attribute=23 zu finden:

SELECT PlayerID
        FROM Player@{force_index=idx_attribute}
        WHERE Attribute = 23
        LIMIT 10

Gemäß der Dokumentation zum Optimieren von Schemadesigns speichert Spanner Indexdaten auf dieselbe Weise wie Tabellen mit einer Zeile pro Indexeintrag. In Lasttests zeigt dieses Modell eine akzeptable Leistung bei der Verteilung der Lese- und Schreiblast des sekundären Index auf mehrere Spanner-Splits. Dies ist im folgenden Diagramm dargestellt:

Nach Attribut auf die Spanner-Server verteilte Spieler

Obwohl die synthetischen Daten aus dem Lasttest dem endgültigen stabilen Zustand des Spiels mit einer guten Verteilung der Attribute-Werte nahekommen, sieht das Spieldesign vor, dass alle Spieler mit Attribute=50 beginnen. Da jeder neue Spieler mit Attribute=50 beginnt, werden neue Spieler in den gleichen Teil des sekundären Indexes, nämlich idx_attribute, eingefügt. Das bedeutet, dass Aktualisierungen an denselben Spanner-Split weitergeleitet werden, wodurch während des Startfensters des Spiels ein Hotspot entsteht. Dies ist eine ineffiziente Nutzung von Spanner.

Spieler mit dem gleichen Attribut beim Start, wodurch in einem einzelnen Spanner-Split ein Hotspot entsteht.

Das folgende Diagramm illustriert, wie das Hotspot-Problem durch Hinzufügen einer IndexPartition-Spalte zum Schema nach dem Start gelöst wird, wodurch die Spieler gleichmäßig auf die verfügbaren Spanner-Splits verteilt werden. Der aktualisierte Befehl zum Erstellen der Tabelle und des Index sieht so aus:

CREATE TABLE Player (
        PlayerID STRING(36) NOT NULL,
        IndexPartition INT64 NOT NULL
        Attribute INT64 NOT NULL
) PRIMARY KEY (PlayerID)

CREATE INDEX idx_attribute ON Player(IndexPartition,Attribute)

Durch Hinzufügen einer IndexPartition-Spalte zum Schema werden die Spieler beim Start gleichmäßig verteilt

Der Wert IndexPartition muss einen begrenzten Bereich haben, damit Abfragen effizient sind. Zugleich muss der Bereich jedoch mindestens doppelt so viele Splits umfassen, um eine effiziente Verteilung zu gewährleisten.

In diesem Fall hat das Studio jedem Spieler in der Spieleanwendung manuell eine IndexPartition zwischen 1 und 6 zugewiesen.

Alternativ könnte jedem Spieler eine Zufallszahl oder ein Wert zugewiesen werden, der sich aus einem Hash zum Wert PlayerID ableitet. Weitere Informationen zu Fragmentierungsstrategien auf Anwendungsebene finden Sie unter Das sollten Datenbankadministratoren über Cloud Spanner wissen, Teil 1: Schlüssel und Indexe.

So lässt sich die vorherige Abfrage aktualisieren, um diesen verbesserten Index zu verwenden:

SELECT PlayerID
        FROM Player@{force_index=idx_attribute}
        WHERE IndexPartition BETWEEN 1 and 6
        AND Attribute = 23
        LIMIT 10

Da kein Betatest durchgeführt wurde, bemerkte das Studio nicht, dass die Tests auf Daten mit falschen Annahmen beruhten. Mit synthetischen Lasttests lässt sich zwar gut überprüfen, wie viele Abfragen pro Sekunde (QPS) eine Instanz verarbeiten kann. Trotzdem ist ein Betatest mit echten Spielern unerlässlich, um das Schema zu validieren und einen erfolgreichen Start vorzubereiten.

Produktionsumgebung für Spitzennachfrage skalieren

Große Spiele haben meist den größten Traffic zu verzeichnen, wenn sie herauskommen. Die Notwendigkeit, eine skalierbares Back-End aufzubauen, gilt aber nicht nur für Plattformdienste und dedizierte Spieleserver, sondern auch für Datenbanken. Mit Google Cloud-Lösungen wie App Engine können Sie Front-End API-Dienste gestalten, die Sie schnell hochskalieren können. Obwohl Sie bei Spanner flexibel Knoten online hinzufügen und entfernen können, ist der Dienst keine Datenbank mit Autoscaling. Sie müssen genügend Knoten bereitstellen, um die Zugriffsspitzen beim Start zu bewältigen.

Anhand der während des Lasttests oder auch bei öffentlichen Betatests gesammelten Daten können Sie die Anzahl der Knoten abschätzen, die Sie beim Start zum Verarbeiten von Anfragen brauchen. Dabei empfiehlt es sich, einige Knoten als Puffer zu ergänzen, falls Sie mehr Spieler als erwartet haben. Die Datenbank sollte immer so dimensioniert werden, dass eine durchschnittliche CPU-Auslastung von 65 % nicht überschritten wird.

Datenbank vor dem Start vorwärmen

Eine andere wichtige Sache, die Sie vor dem Start vorbereiten müssen, ist das Aufwärmen der Datenbank.

Im kalten Zustand kann Spanner die Knoten, die Sie zum Start benötigen, nicht bereitstellen. Das System muss vorher aufgewärmt werden.

Spanner ist eine verteilte Datenbank, d. h., wenn eine Datenbank wächst, teilt Spanner die Daten in Portionen auf, sogenannte Splits. Aufteilungen können sich unabhängig voneinander bewegen und verschiedenen Servern zugewiesen werden, die sich an verschiedenen physischen Standorten befinden können. Weitere Informationen finden Sie unter Datenbankaufteilungen.

Eine Aufteilung ist als ein Bereich von Zeilen definiert. Sie enthält also eine Teilmenge einer Tabelle. Spanner teilt Daten anhand von Last und Größe auf. Auf diese Weise können Aufteilungen dynamisch in Spanner-Knoten verschoben werden, um für eine ausgeglichene Gesamtlast der Datenbank zu sorgen. Je mehr Daten Sie in Spanner einfügen, desto mehr Splits werden generiert.

Das folgende Diagramm zeigt vier Knoten.

Daten in einem Knoten bei Spanner im kalten Zustand

Da sich zu Beginn des Schreibens von Daten keine Daten in Spanner befinden, wird nur auf einen einzelnen Knoten geschrieben. Spanner befindet sich derzeit im kalten Zustand.

Das folgende Diagramm veranschaulicht die Aufteilung auf die anderen Knoten.

Auf Knoten aufgeteilte Daten bei Cloud Spanner im kalten Zustand

Sobald die Daten in das System gelangen, beginnt Cloud Spanner mit deren Aufteilung, um die Last auf die vier bereitgestellten Knoten zu verteilen. Jetzt befindet sich Spanner im warmen Zustand.

Sie sollten Ihr Spiel starten, wenn sich Cloud Spanner in einem warmen Zustand befindet und die Aufteilung auf alle Knoten erfolgt ist. So wärmen Sie die Datenbank auf:

  1. Achten Sie darauf, dass sich die für den Lasttest erzeugten Primärschlüssel der Tabellen im selben Schlüsselbereich befinden und dieselben statistischen Eigenschaften haben wie die Schlüssel, die Sie für echten Produktionstraffic verwenden.
  2. Führen Sie zwei Tage vor Start des Spiels einen Lasttest durch. Dieser sollte mindestens eine Stunde lang unter der erwarteten Spitzenlast laufen. Durch den Lasttest erstellt Spanner aufgrund des lastbasierten Aufteilungsprinzips mehr Aufteilungen.
  3. Nach Abschluss des Lasttests können Sie die vom Lasttest erstellten Zeilen aus den Tabellen löschen. Löschen Sie jedoch nicht die Tabellen selbst. So bleiben die Aufteilungen für das Startfenster verfügbar.

Leistung überwachen und nachvollziehen

Jede Produktionsdatenbank braucht umfangreiche Messwerte für die Überwachung und Leistung. Spanner hat integrierte Messwerte in Cloud Monitoring. Die bereitgestellten gRPC-Bibliotheken sollten möglichst in den Back-End-Prozess des Spiels eingebunden werden, da diese Bibliotheken OpenCensus Tracing enthalten. Mit OpenCensus Tracing können Sie Abfrage-Traces in Cloud Trace sowie in anderen unterstützten Open Source-Tracing-Tools ansehen.

In Cloud Monitoring werden Details zur Nutzung von Spanner, einschließlich der Datenspeicherung und CPU-Auslastung, angezeigt. In den meisten Fällen empfiehlt es sich, bei der Entscheidung zur Skalierung von Spanner auf diesen Messwert zur CPU-Nutzung oder die beobachtete Latenz zurückzugreifen. Weitere Informationen zu CPU-Nutzungvorschlägen für eine optimierte Leistung finden Sie unter Best Practices für Instanzen.

Spanner bietet Abfrageausführungspläne. Sie können diese Pläne in der Cloud Console überprüfen. Bei Bedarf hilft Ihnen der Support, Ihre Abfrageleistung nachzuvollziehen.

Wenn Sie eine Leistungsbewertung vornehmen, sollten Sie Kurzzeittests auf ein Minimum beschränken, da Spanner die Daten im Hintergrund transparent aufteilt, um die Leistung anhand Ihrer Datenzugriffsmuster zu optimieren. Die Leistung sollten Sie mithilfe von dauerhaften, realistischen Abfragelasten evaluieren.

Beim Entfernen von Daten Zeilen löschen, statt Tabellen neu zu erstellen

Wenn Sie mit Spanner arbeiten, hatten neu erstellte Tabellen noch keine Gelegenheit, last- oder größenbasierte Aufteilungen durchzuführen, um die Leistung zu verbessern. Wenn Sie Daten löschen, indem Sie eine Tabelle entfernen, und dann eine neue Tabelle erstellen, braucht Spanner Daten, Abfragen und Zeit, um die korrekten Splits für die Tabelle zu ermitteln. Wenn Sie eine Tabelle mit der gleichen Art von Daten neu füllen möchten, was zum Beispiel bei aufeinanderfolgenden Leistungstests der Fall sein kann, können Sie stattdessen eine DELETE-Abfrage für die Zeilen ausführen, die nicht mehr benötigte Daten enthalten. Aus demselben Grund sollte bei Schemaaktualisierungen auf die bereitgestellte Cloud Spanner API zurückgegriffen werden. Außerdem sollten Sie hier eine manuelle Strategie, wie das Erstellen einer neuen Tabelle und das Kopieren der Daten aus einer anderen Tabelle oder einer Sicherungsdatei, vermeiden.

Aufbewahrungsort für Daten auswählen, um Compliance-Anforderungen zu erfüllen

Viele Spiele müssen, wenn sie weltweit gespielt werden, Gesetze zum Aufbewahrungsort von Daten wie die DSGVO einhalten. Das Whitepaper über Google Cloud und die DSGVO kann Ihnen dabei helfen, Ihre Anforderungen bezüglich der DSGVO zu ermitteln, damit Sie die richtige regionale Konfiguration für Spanner auswählen können.

Nächste Schritte