In diesem Dokument werden Methoden für Datenbankadministratoren und Anwendungsentwickler beschrieben, mit denen eindeutige numerische Sequenzen in Anwendungen, die Spanner verwenden, generiert werden können.
Einführung
Es gibt oft Situationen, in denen ein Unternehmen eine einfache, eindeutige numerische ID benötigt, z. B. eine Mitarbeiternummer oder eine Rechnungsnummer. Konventionelle relationale Datenbanken enthalten oft ein Feature zum Generieren eindeutiger, monoton ansteigender Nummernsequenzen. Diese Sequenzen werden verwendet, um eindeutige Kennungen (Zeilenschlüssel) für Objekte zu generieren, die in der Datenbank gespeichert sind.
Die Verwendung monoton ansteigender (oder abnehmender) Werte als Zeilenschlüssel entspricht möglicherweise nicht den Best Practices in Spanner, da dies Hotspots in der Datenbank erzeugt und zu einer reduzierten Leistung führt. In diesem Dokument werden Mechanismen zur Implementierung eines Sequenzgenerators mithilfe einer Spanner-Datenbanktabelle und Logik auf Anwendung vorgeschlagen.
Alternativ unterstützt Spanner einen integrierten Bit-Umkehr-Sequenzgenerator. Weitere Informationen zum Spanner-Sequenzgenerator finden Sie unter Sequenzen erstellen und verwalten.
Anforderungen für einen Sequenzgenerator
Jeder Sequenzgenerator muss für jede Transaktion einen eindeutigen Wert generieren.
Je nach Anwendungsfall muss ein Sequenzgenerator möglicherweise auch Sequenzen mit den folgenden Eigenschaften erstellen:
- Geordnet: Niedrigere Werte in der Sequenz dürfen nicht nach höheren Werten ausgegeben werden.
- Lückenlos: In der Sequenz darf es keine Lücken geben.
Der Sequenzgenerator muss außerdem Werte mit der für die Anwendung erforderlichen Häufigkeit generieren.
All diese Anforderungen zu erfüllen, kann sich als schwierig erweisen, insbesondere in einem verteilten System. Sie können aber Kompromisse bei den Anforderungen an eine geordnete und lückenlose Sequenz eingehen, wenn dies zum Erreichen Ihrer Leistungsziele erforderlich ist.
Andere Datenbank-Engines bieten Möglichkeiten, um diese Anforderungen zu erfüllen. Beispielsweise können Sequenzen in PostgreSQL-Spalten und AUTO_INCREMENT-Spalten in MySQL eindeutige Werte für separate Transaktionen generieren, allerdings keine lückenlosen Werte, wenn für Transaktionen ein Rollback durchgeführt wird. Weitere Informationen finden Sie in den Hinweisen in der PostgreSQL-Dokumentation und im Abschnitt zu den AUTO_INCREMENT-Auswirkungen in MySQL.
Sequenzgeneratoren, die Zeilen von Datenbanktabellen verwenden
Ihre Anwendung kann einen Sequenzgenerator mithilfe einer Datenbanktabelle implementieren, in der die Sequenznamen und der nächste Wert in der Sequenz gespeichert werden.
Beim Lesen und Erhöhen der Zelle next_value
einer Sequenz in einer Datenbanktransaktion werden eindeutige Werte generiert, ohne dass eine weitere Synchronisierung zwischen Anwendungsprozessen erforderlich ist.
Definieren Sie zuerst die Tabelle so:
CREATE TABLE sequences (
name STRING(64) NOT NULL,
next_value INT64 NOT NULL,
) PRIMARY KEY (name)
Zum Erstellen von Sequenzen können Sie eine Zeile mit dem neuen Sequenznamen und dem Startwert in die Tabelle einfügen, z. B. ("invoice_id", 1)
. Da jedoch die Zelle next_value
für jeden generierten Sequenzwert erhöht wird, ist die Leistung durch die Häufigkeit begrenzt, mit der die Zeile aktualisiert werden kann.
Spanner-Clientbibliotheken verwenden wiederholbare Transaktionen, um Konflikte zu lösen. Wenn Zellen (Spaltenwerte), die während einer Lese-/Schreibtransaktion gelesen werden, an anderer Stelle geändert werden, wird die Transaktion blockiert, bis die andere Transaktion abgeschlossen ist. Dann wird die Transaktion abgebrochen und wiederholt, sodass die aktualisierten Werte gelesen werden. Dies minimiert die Dauer von Schreibsperren, bedeutet aber auch, dass eine Transaktion mehrmals wiederholt werden kann, bevor ein Commit erfolgreich durchgeführt wird.
Da für eine Zeile jeweils nur eine Transaktion stattfinden kann, ist die maximale Häufigkeit der Ausgabe von Sequenzwerten umgekehrt proportional zur Gesamtlatenz der Transaktion.
Diese Gesamtlatenz der Transaktion hängt von mehreren Faktoren ab, z. B. der Latenz zwischen der Clientanwendung und den Spanner-Knoten, der Latenz zwischen den Spanner-Knoten und der TrueTime. Eine multiregionale Konfiguration hat beispielsweise eine höhere Transaktionslatenz, da sie auf ein Quorum von Schreibbestätigungen von den Knoten in verschiedenen Regionen warten muss, um den Vorgang abzuschließen.
Beispiel: Wenn eine Lese-/Aktualisierungstransaktion für eine einzelne Zelle (eine Spalte in einer einzelnen Zeile) eine Latenz von 10 Millisekunden (ms) hat, beträgt die maximale theoretische Häufigkeit der Ausgabe von Sequenzwerten 100 pro Sekunde. Dieser Höchstwert gilt für die gesamte Datenbank, unabhängig von der Anzahl der Clientanwendungsinstanzen oder der Anzahl der Knoten in der Datenbank. Dies liegt daran, dass eine einzelne Zeile immer von einem einzelnen Knoten verwaltet wird.
Im folgenden Abschnitt wird beschrieben, wie Sie diese Einschränkung umgehen können.
Anwendungsseitige Implementierung
Der Anwendungscode muss die Zelle next_value
in der Datenbank lesen und aktualisieren. Dafür gibt es mehrere Möglichkeiten, die jeweils unterschiedliche Leistungsmerkmale und Nachteile haben.
Einfacher Sequenzgenerator innerhalb von Transaktionen
Die einfachste Möglichkeit zur Verarbeitung der Sequenzgenerierung besteht darin, den Spaltenwert innerhalb der Transaktion zu erhöhen, wenn die Anwendung einen neuen sequenziellen Wert benötigt.
In einer einzelnen Transaktion führt die Anwendung Folgendes aus:
- Liest die Zelle
next_value
, um den in der Anwendung zu verwendenden Sequenznamen zu ermitteln. - Erhöht und aktualisiert die Zelle
next_value
für den Sequenznamen. - Verwendet den abgerufenen Wert für den Spaltenwert, den die Anwendung benötigt.
- Führt den Rest der Anwendungstransaktion aus.
Durch diesen Prozess wird eine Sequenz generiert, die geordnet und lückenlos ist. Wenn die Zelle next_value
in der Datenbank durch keine Aktion auf einen niedrigeren Wert aktualisiert wird, ist die Sequenz außerdem eindeutig.
Da der Sequenzwert als Teil der größeren Anwendungstransaktion abgerufen wird, hängt die maximale Häufigkeit der Sequenzgenerierung von der Komplexität der gesamten Anwendungstransaktion ab. Eine komplexe Transaktion hat eine höhere Latenz und daher eine geringere maximal mögliche Häufigkeit.
In einem verteilten System können viele Transaktionsversuche gleichzeitig erfolgen, was zu einer großen Anzahl von Sequenzwertkonflikten führt. Da die Zelle next_value
innerhalb der Anwendungstransaktion aktualisiert wird, werden alle anderen Transaktionen, die gleichzeitig die Zelle next_value
erhöhen möchten, von der ersten Transaktion blockiert und wiederholt.
Dies kann die Zeit, die die Anwendung zur erfolgreichen Ausführung der Transaktion benötigt, erheblich erhöhen und zu Leistungsproblemen führen.
Der folgende Code ist ein Beispiel für einen einfachen Sequenzgenerator innerhalb einer Transaktion, der pro Transaktion nur einen einzigen Sequenzwert zurückgibt. Diese Einschränkung ergibt sich daraus, dass Schreibvorgänge innerhalb einer mit der Mutation API ausgeführten Transaktion erst nach dem Commit der Transaktion sichtbar sind, auch für Lesevorgänge in derselben Transaktion. Daher wird immer derselbe Sequenzwert zurückgegeben, wenn Sie diese Funktion mehrmals in derselben Transaktion aufrufen.
Der folgende Beispielcode zeigt, wie eine synchrone getNext()
-Funktion implementiert wird:
Der folgende Beispielcode zeigt, wie die synchrone getNext()
-Funktion in einer Transaktion verwendet wird:
Verbesserter synchroner Sequenzgenerator innerhalb von Transaktionen
Sie können die vorherige Abstraktion so ändern, dass mehrere Werte innerhalb einer einzelnen Transaktion erzeugt werden. Dazu verfolgen Sie die Sequenzwerte, die innerhalb einer Transaktion ausgegeben werden.
In einer einzelnen Transaktion führt die Anwendung Folgendes aus:
- Liest die Zelle
next_value
, um den in der Anwendung zu verwendenden Sequenznamen zu ermitteln. - Speichert diesen Wert intern als Variable.
- Erhöht jedes Mal, wenn ein neuer Sequenzwert angefordert wird, die gespeicherte Variable
next_value
und speichert einen Schreibvorgang zwischen, der den aktualisierten Zellenwert in der Datenbank festlegt. - Führt den Rest der Anwendungstransaktion aus.
Wenn Sie eine Abstraktion verwenden, muss das Objekt für diese Abstraktion innerhalb der Transaktion erstellt werden. Das Objekt führt einen einzelnen Lesevorgang aus, wenn der erste Wert angefordert wird. Das Objekt verfolgt die Zelle next_value
intern, sodass mehr als ein Wert generiert werden kann.
Für diese Version gelten dieselben Einschränkungen in Bezug auf Latenz und Konflikte wie bei der vorherigen Version.
Der folgende Beispielcode zeigt, wie eine synchrone getNext()
-Funktion implementiert wird:
Der folgende Beispielcode zeigt, wie die synchrone getNext()
-Funktion in einer Anfrage für zwei Sequenzwerte verwendet wird:
(Asynchroner) Sequenzgenerator außerhalb von Transaktionen
In den vorherigen beiden Implementierungen hängt die Leistung des Generators von der Latenz der Anwendungstransaktion ab. Wenn Sie die Sequenz in einer separaten Transaktion erhöhen, können Sie die maximale Häufigkeit verbessern, müssen dann allerdings Lücken in der Sequenz in Kauf nehmen. Dies ist der von PostgreSQL verwendete Ansatz. Bevor die Anwendung die Transaktion startet, sollten Sie zuerst die zu verwendenden Sequenzwerte abrufen.
Die Anwendung führt Folgendes aus:
- Erstellt zuerst eine Transaktion zum Abrufen und Aktualisieren des Sequenzwerts:
- Liest die Zelle
next_value
, um den in der Anwendung zu verwendenden Sequenznamen zu ermitteln. - Speichert diesen Wert als Variable.
- Erhöht und aktualisiert die Zelle
next_value
in der Datenbank für den Sequenznamen. - Schließt die Transaktion ab.
- Liest die Zelle
- Verwendet den zurückgegebenen Wert in einer separaten Transaktion.
Die Latenz dieser separaten Transaktion liegt nahe an der minimalen Latenz, wobei sich die Leistung der maximalen theoretischen Häufigkeit von 100 Werten pro Sekunde nähert (bei einer angenommenen Transaktionslatenz von 10 ms). Da die Sequenzwerte separat abgerufen werden, ändert sich die Latenz der Anwendungstransaktion selbst nicht und Konflikte werden minimiert.
Wenn jedoch ein Sequenzwert angefordert und nicht verwendet wird, bleibt in der Sequenz eine Lücke, da kein Rollback für angeforderte Sequenzwerte durchgeführt werden kann. Dies kann auftreten, wenn die Anwendung während der Transaktion nach dem Anfordern eines Sequenzwerts abgebrochen wird oder fehlschlägt.
Der folgende Beispielcode zeigt, wie eine Funktion implementiert wird, die die Zelle next_value
in der Datenbank abruft und erhöht:
Mit dieser Funktion können Sie ganz einfach einen einzelnen neuen Sequenzwert abrufen, wie in der folgenden Implementierung einer asynchronen getNext()
-Funktion gezeigt:
Der folgende Beispielcode zeigt, wie die asynchrone getNext()
-Funktion in einer Anfrage für zwei Sequenzwerte verwendet wird:
Im vorherigen Codebeispiel sehen Sie, dass die Sequenzwerte außerhalb der Anwendungstransaktion angefordert werden. Dies liegt daran, dass Cloud Spanner die Ausführung einer Transaktion innerhalb einer anderen Transaktion im selben Thread (auch als verschachtelte Transaktionen bezeichnet) nicht unterstützt.
Sie können diese Einschränkung umgehen, wenn Sie den Sequenzwert mithilfe eines Hintergrundthreads anfordern und auf das Ergebnis warten:
Batchsequenzgenerator
Sie können eine erhebliche Leistungsverbesserung erzielen, wenn Sie auch auf die Anforderung verzichten, dass Sequenzwerte geordnet sein müssen. Dadurch kann die Anwendung einen Batch von Sequenzwerten reservieren und intern ausgeben. Einzelne Anwendungsinstanzen haben einen eigenen separaten Batch von Werten, sodass die Werte nicht der Reihe nach ausgegeben werden. Außerdem hinterlassen Anwendungsinstanzen, die nicht ihren gesamten Batch von Werten verwenden (beispielsweise beim Herunterfahren der Anwendungsinstanz), Lücken durch nicht verwendete Werte in der Sequenz.
Die Anwendung führt Folgendes aus:
- Behält für jede Sequenz einen internen Status bei, der den Startwert und die Größe des Batches sowie den nächsten verfügbaren Wert enthält.
- Fordert einen Sequenzwert aus dem Batch an.
- Führt Folgendes aus, wenn im Batch keine Werte mehr vorhanden sind:
- Erstellt eine Transaktion, um den Sequenzwert zu lesen und zu aktualisieren.
- Liest die Zelle
next_value
für die Sequenz. - Speichert diesen Wert intern als Startwert des neuen Batches.
- Erhöht die Zelle
next_value
in der Datenbank um einen Betrag, der der Batchgröße entspricht. - Schließt die Transaktion ab.
- Gibt den nächsten verfügbaren Wert zurück und erhöht den internen Status.
- Verwendet den zurückgegebenen Wert in der Transaktion.
Bei dieser Methode kommt es bei Transaktionen, die einen Sequenzwert verwenden, nur dann zu einer erhöhten Latenz, wenn ein neuer Batch von Sequenzwerten reserviert werden muss.
Der Vorteil besteht darin, dass durch Erhöhen der Batchgröße die Leistung auf ein beliebiges Niveau gesteigert werden kann, da der begrenzende Faktor die Anzahl der pro Sekunde ausgegebenen Batches ist.
Bei einer Batchgröße von 100 – einer angenommenen Latenz von 10 ms zum Abrufen eines neuen Batches und somit maximal 100 Batches pro Sekunde – können beispielsweise 10.000 Sequenzwerte pro Sekunde ausgegeben werden.
Der folgende Beispielcode zeigt, wie eine getNext()
-Funktion mithilfe von Batches implementiert wird. Beachten Sie, dass der Code die zuvor definierte getAndIncrementNextValueInDB()
-Funktion wiederverwendet, um neue Batches von Sequenzwerten aus der Datenbank abzurufen.
Der folgende Beispielcode zeigt, wie die asynchrone getNext()
-Funktion in einer Anfrage für zwei Sequenzwerte verwendet wird:
Auch hier müssen die Werte außerhalb der Transaktion (oder mithilfe eines Hintergrundthreads) angefordert werden, da Spanner keine verschachtelten Transaktionen unterstützt.
Asynchroner Batchsequenzgenerator
Sie können die Leistung des vorherigen Batchgenerators für Hochleistungsanwendungen verbessern, bei denen eine erhöhte Latenz nicht akzeptabel ist. In diesem Fall müssen Sie dafür sorgen, dass ein neuer Batch von Werten bereitsteht, wenn der aktuelle Batch von Werten aufgebraucht ist.
Dazu legen Sie einen Schwellenwert fest, der angibt, wann die Anzahl der verbliebenen Sequenzwerte in einem Batch zu niedrig ist. Wenn der Schwellenwert erreicht ist, fordert der Sequenzgenerator einen neuen Batch von Werten in einem Hintergrundthread an.
Wie bei der vorherigen Version werden die Werte nicht der Reihe nach ausgegeben. Wenn Transaktionen fehlschlagen oder Anwendungsinstanzen heruntergefahren werden, entstehen außerdem Lücken durch die nicht verwendeten Werte in der Sequenz.
Die Anwendung führt Folgendes aus:
- Behält für jede Sequenz einen internen Status bei, der den Startwert des Batches sowie den nächsten verfügbaren Wert enthält.
- Fordert einen Sequenzwert aus dem Batch an.
- Führt Folgendes in einem Hintergrundthread aus, wenn die Anzahl der verbliebenen Werte im Batch kleiner als der Schwellenwert ist:
- Erstellt eine Transaktion, um den Sequenzwert zu lesen und zu aktualisieren.
- Liest die Zelle
next_value
, um den in der Anwendung zu verwendenden Sequenznamen zu ermitteln. - Speichert diesen Wert intern als Startwert des nächsten Batches.
- Erhöht die Zelle
next_value
in der Datenbank um einen Betrag, der der Batchgröße entspricht. - Schließt die Transaktion ab.
- Ruft den Startwert des nächsten Batches aus dem Hintergrundthread ab (und wartet gegebenenfalls auf dessen Abschluss), wenn im Batch keine Werte mehr vorhanden sind, und erstellt einen neuen Batch mit dem abgerufenen Startwert als nächsten Wert.
- Gibt den nächsten Wert zurück und erhöht den internen Status.
- Verwendet den zurückgegebenen Wert in der Transaktion.
Damit Sie eine optimale Leistung erzielen, sollte der Hintergrundthread gestartet und abgeschlossen werden, bevor die Sequenzwerte im aktuellen Batch aufgebraucht sind. Andernfalls muss die Anwendung auf den nächsten Batch warten, was die Latenz erhöht. Daher müssen Sie die Batchgröße und den unteren Schwellenwert abhängig von der Häufigkeit der ausgegebenen Sequenzwerte anpassen.
Gehen Sie beispielsweise von einer Transaktionszeit von 20 ms zum Abrufen eines neuen Batches von Werten, einer Batchgröße von 1.000 und einer maximalen Sequenzausgabehäufigkeit von 500 Werten pro Sekunde (ein Wert alle 2 ms) aus. In diesem Fall werden während der 20 ms, in denen ein neuer Batch von Werten ausgegeben wird, 10 Sequenzwerte ausgegeben. Daher sollte der Schwellenwert für die Anzahl der verbliebenen Sequenzwerte größer als 10 sein, damit der nächste Batch bei Bedarf verfügbar ist.
Der folgende Beispielcode zeigt, wie eine getNext()
-Funktion mithilfe von Batches implementiert wird. Beachten Sie, dass der Code die zuvor definierte getAndIncrementNextValueInDB()
-Funktion verwendet, um einen Batch von Sequenzwerten mithilfe eines Hintergrundthreads abzurufen.
Der folgende Beispielcode zeigt, wie die asynchrone getNext()
-Batchfunktion in einer Anfrage für zwei Werte zur Verwendung in der Transaktion eingesetzt wird:
In diesem Fall können die Werte innerhalb der Transaktion angefordert werden, da der Abruf eines neuen Batches von Werten in einem Hintergrundthread erfolgt.
Zusammenfassung
In der folgenden Tabelle werden die Eigenschaften der vier Typen von Sequenzgeneratoren verglichen:
Synchron | Asynchron | Batch | Asynchroner Batch | |
---|---|---|---|---|
Eindeutige Werte | Ja | Ja | Ja | Ja |
Global geordnete Werte | Ja | Ja | Nein Bei einer ausreichend hohen Last und einer ausreichend kleinen Batchgröße liegen die Werte jedoch nahe beieinander. |
Nein Bei einer ausreichend hohen Last und einer ausreichend kleinen Batchgröße liegen die Werte jedoch nahe beieinander. |
Lückenlos | Ja | Nein | Nein | Nein |
Leistung | 1/Transaktionslatenz, (ungefähr 25 Werte pro Sekunde) |
50 bis 100 Werte pro Sekunde | 50 bis 100 Batches von Werten pro Sekunde | 50 bis 100 Batches von Werten pro Sekunde |
Latenzanstieg | Mehr als 10 ms Erheblich höher mit großer Anzahl von Konflikten (wenn eine Transaktion sehr lange dauert) |
10 ms bei jeder Transaktion Erheblich höher mit großer Anzahl von Konflikten |
10 ms, aber nur, wenn ein neuer Batch von Werten abgerufen wird | Null, wenn die Batchgröße und der untere Schwellenwert auf geeignete Werte festgelegt sind |
Die obige Tabelle zeigt auch, dass Sie bei den Anforderungen an global geordnete Werte und lückenlose Wertereihen unter Umständen Kompromisse eingehen müssen, um eindeutige Werte zu generieren und gleichzeitig allgemeine Leistungsanforderungen zu erfüllen.
Leistungstests
Sie können ein Leistungstest-/Analysetool verwenden, das sich im selben GitHub-Repository wie die vorherigen Sequenzgeneratorklassen befindet, um jeden dieser Sequenzgeneratoren zu testen und Leistungs- und Latenzmerkmale zu demonstrieren. Das Tool simuliert eine Anwendungstransaktionslatenz von 10 ms und führt mehrere Threads gleichzeitig aus, die Sequenzwerte anfordern.
Für Leistungstests wird nur eine Spanner-Instanz mit einem einzigen Knoten benötigt, da lediglich eine einzige Zeile geändert wird.
Die folgende Ausgabe zeigt beispielsweise einen Vergleich zwischen Leistung und Latenz im synchronen Modus mit 10 Threads:
$ ITERATIONS=2000
$ MODE=SYNC
$ NUMTHREADS=10
$ java -jar sequence-generator.jar \
$INSTANCE_ID $DATABASE_ID $MODE $ITERATIONS $NUMTHREADS
2000 iterations (10 parallel threads) in 58739 milliseconds: 34.048928 values/s
Latency: 50%ile 27 ms
Latency: 75%ile 31 ms
Latency: 90%ile 1189 ms
Latency: 99%ile 2703 ms
In der folgenden Tabelle werden die Ergebnisse für verschiedene Modi und Anzahlen von parallelen Threads verglichen, einschließlich der Anzahl der Werte, die pro Sekunde ausgegeben werden kann, und der Latenz beim 50., 90. und 99. Perzentil:
Modus und Parameter | Anzahl der Threads | Werte/Sek. | Latenz beim 50. Perzentil (ms) | Latenz beim 90. Perzentil (ms) | Latenz beim 99. Perzentil (ms) |
---|---|---|---|---|---|
SYNC | 10 | 34 | 27 | 1189 | 2703 |
SYNC | 50 | 30,6 | 1191 | 3513 | 5982 |
ASYNC | 10 | 66,5 | 28 | 611 | 1460 |
ASYNC | 50 | 78,1 | 29 | 1695 | 3442 |
BATCH (Größe 200) |
10 | 494 | 18 | 20 | 38 |
BATCH (Batchgröße 200) | 50 | 1195 | 27 | 55 | 168 |
ASYNC BATCH (Batchgröße 200, LT 50) |
10 | 512 | 18 | 20 | 30 |
ASYNC BATCH (Batchgröße 200, LT 50) |
50 | 1622 | 24 | 28 | 30 |
Sie sehen, dass im synchronen Modus (SYNC) mit der höheren Anzahl von Threads mehr Konflikte auftreten. Dies führt zu deutlich höheren Transaktionslatenzen.
Im asynchronen Modus (ASYNC) gibt es weniger Konflikte und die Häufigkeit ist höher, da die Transaktion zum Abrufen der Sequenz kleiner und von der Anwendungstransaktion getrennt ist. Es kann jedoch trotzdem zu Konflikten kommen, was zu höheren Latenzen beim 90. Perzentil führt.
Im Batchmodus (BATCH) wird die Latenz erheblich reduziert, mit Ausnahme des 99. Perzentils, da der Generator dort einen weiteren Batch von Sequenzwerten aus der Datenbank synchron anfordern muss. Die Leistung ist im BATCH-Modus um ein Vielfaches höher als im ASYNC-Modus.
Der Batchmodus mit 50 Threads hat eine höhere Latenz, da Sequenzen so schnell ausgegeben werden, dass der begrenzende Faktor die Leistung der VM-Instanz ist (in diesem Fall lief während des Tests eine Maschine mit 4 vCPUs bei 350 % CPU). Wenn Sie mehrere Maschinen und mehrere Prozesse verwenden, erhalten Sie ähnliche Gesamtergebnisse wie beim Batchmodus mit 10 Threads.
Im ASYNC BATCH-Modus ist die Latenzschwankung minimal und die Leistung höher – selbst bei einer großen Anzahl von Threads –, da die Latenz beim Anfordern eines neuen Batches aus der Datenbank völlig unabhängig von der Anwendungstransaktion ist.
Nächste Schritte
- Mehr über Best Practices für das Schemadesign in Spanner erfahren
- Schlüssel und Indexe für Spanner-Tabellen auswählen
- Referenzarchitekturen, Diagramme und Best Practices zu Google Cloud kennenlernen. Weitere Informationen zu Cloud Architecture Center