In diesem Dokument finden Sie eine Anleitung zum Skalieren Ihrer serverlosen App über Tausende von Vorgängen pro Sekunde oder Hunderttausende von gleichzeitigen Nutzern hinaus. Dieses Dokument enthält fortgeschrittene Themen, die Ihnen helfen, das System im Detail zu verstehen. Wenn Sie gerade erst mit Cloud Firestore beginnen, lesen Sie stattdessen die Kurzanleitung.
Cloud Firestore und die mobilen/Web-SDKs von Firebase bieten ein leistungsstarkes Modell für die Entwicklung serverloser Apps, bei denen clientseitiger Code direkt auf die Datenbank zugreift. Mit den SDKs können Clients in Echtzeit auf Aktualisierungen der Daten warten. Sie können Echtzeitaktualisierungen verwenden, um reaktionsschnelle Apps zu erstellen, die keine Serverinfrastruktur erfordern. Es ist zwar sehr einfach, etwas zum Laufen zu bringen, aber es hilft die Einschränkungen in den Systemen zu verstehen, aus denen Cloud Firestore besteht, damit Ihre serverlose App bei steigendem Traffic gut skaliert und eine gute Leistung erbringt.
In den folgenden Abschnitten finden Sie Ratschläge zum Skalieren Ihrer App.
Datenbankstandort in der Nähe Ihrer Nutzer auswählen
Das folgende Diagramm zeigt die Architektur einer Echtzeit-App:
Wenn eine App, die auf dem Gerät eines Nutzers (mobil oder im Web) ausgeführt wird, eine
Verbindung zu Cloud Firestore herstellt, wird die Verbindung zu einem
Cloud Firestore Frontend-Server in derselben
Region weitergeleitet, in der sich Ihre Datenbank befindet. Wenn sich Ihre Datenbank beispielsweise
in us-east1 befindet, wird die Verbindung auch zu einem
Cloud Firestore Frontend in us-east1 weitergeleitet. Diese Verbindungen sind
langlebig und bleiben geöffnet, bis sie von der App explizit geschlossen werden. Das
Frontend liest Daten aus den zugrunde liegenden Cloud Firestore Speichersystemen.
Die Entfernung zwischen dem physischen Standort eines Nutzers und dem Cloud Firestore Datenbankstandort wirkt sich auf die Latenz aus, die der Nutzer erlebt. Ein Nutzer in Indien, dessen App mit einer Datenbank in einer Google Cloud Region in Nordamerika kommuniziert, empfindet die App möglicherweise als langsamer und weniger reaktionsschnell, als wenn sich die Datenbank näher befindet, z. B. in Indien oder in einem anderen Teil Asiens.
Einhaltung von Zuverlässigkeitsvorgaben
Die folgenden Themen verbessern oder beeinflussen die Zuverlässigkeit Ihrer App:
Offlinemodus aktivieren
Die Firebase SDKs bieten Offlinedatenpersistenz. Wenn die App auf dem Gerät des Nutzers keine Verbindung zu Cloud Firestore herstellen kann, bleibt die App nutzbar, indem sie mit lokal im Cache gespeicherten Daten arbeitet. So ist der Datenzugriff auch dann gewährleistet, wenn Nutzer zeitweise keine Internetverbindung haben oder den Zugriff für mehrere Stunden oder Tage vollständig verlieren. Weitere Informationen zum Offlinemodus finden Sie unter Offlinedaten aktivieren.
Automatische Wiederholungsversuche
Die Firebase SDKs kümmern sich um Wiederholungsversuche für Vorgänge und das Wiederherstellen unterbrochener Verbindungen. So können vorübergehende Fehler umgangen werden, die durch das Neustarten von Servern oder Netzwerkprobleme zwischen dem Client und der Datenbank verursacht werden.
Zwischen regionalen und multiregionalen Standorten wählen
Bei der Auswahl zwischen regionalen und multiregionalen Standorten müssen mehrere Kompromisse eingegangen werden. Der Hauptunterschied besteht darin, wie Daten repliziert werden. Dies wirkt sich auf die Verfügbarkeitsgarantien Ihrer App aus. Eine multiregionale Instanz bietet eine höhere Zuverlässigkeit und erhöht die Lebensdauer Ihrer Daten, aber der Kompromiss sind die Kosten.
Echtzeit-Abfragesystem
Mit Echtzeitabfragen, auch Snapshot-Listener genannt, kann die App auf Änderungen in der Datenbank warten und Benachrichtigungen mit niedriger Latenz erhalten, sobald sich die Daten ändern. Eine App kann dasselbe Ergebnis erzielen, indem sie die Datenbank regelmäßig auf Aktualisierungen abfragt. Das ist jedoch oft langsamer, teurer und erfordert mehr Code. Beispiele zum Einrichten und Verwenden von Echtzeitabfragen finden Sie unter Echtzeitaktualisierungen erhalten. In den folgenden Abschnitten wird im Detail beschrieben, wie Snapshot-Listener funktionieren, und es werden einige Best Practices zum Skalieren von Echtzeitabfragen bei gleichbleibender Leistung beschrieben.
Stellen Sie sich zwei Nutzer vor, die über eine Messaging App, die mit einem der mobilen SDKs erstellt wurde, eine Verbindung zu Cloud Firestore herstellen.
Client A schreibt in die Datenbank, um Dokumente in einer Sammlung namens chatroom hinzuzufügen und zu aktualisieren:
collection chatroom:
document message1:
from: 'Sparky'
message: 'Welcome to Cloud Firestore!'
document message2:
from: 'Santa'
message: 'Presents are coming'
Client B wartet mit einem Snapshot-Listener auf Aktualisierungen in derselben Sammlung. Client B erhält sofort eine Benachrichtigung, wenn jemand eine neue Nachricht erstellt. Das folgende Diagramm zeigt die Architektur hinter einem Snapshot-Listener:
Die folgende Ereignissequenz findet statt, wenn Client B einen Snapshot-Listener mit der Datenbank verbindet:
- Client B öffnet eine Verbindung zu Cloud Firestore und registriert einen
Listener, indem er über
das Firebase SDK einen Aufruf an
onSnapshot(collection("chatroom"))sendet. Dieser Listener kann stundenlang aktiv bleiben. - Das Cloud Firestore Frontend fragt das zugrunde liegende Speichersystem ab, um das Dataset zu bootstrappen. Es lädt das gesamte Ergebnisset übereinstimmender Dokumente. Wir bezeichnen dies als Polling-Abfrage. Das System wertet dann die Datenbank's Firebase-Sicherheitsregeln aus, um zu prüfen, ob der Nutzer auf diese Daten zugreifen kann. Wenn der Nutzer autorisiert ist, gibt die Datenbank die Daten an den Nutzer zurück.
- Die Abfrage von Client B wechselt dann in den Listen-Modus. Der Listener registriert sich bei einem Abo-Handler und wartet auf Aktualisierungen der Daten.
- Client A sendet jetzt einen Schreibvorgang, um ein Dokument zu ändern.
- Die Datenbank übergibt die Dokumentänderung an ihr Speichersystem.
- Transaktionsweise übergibt das System dieselbe Aktualisierung an ein internes Änderungsprotokoll. Das Änderungsprotokoll legt eine strikte Reihenfolge der Änderungen fest, sobald sie auftreten.
- Das Änderungsprotokoll verteilt die aktualisierten Daten wiederum an einen Pool von Abo-Handlern.
- Ein Reverse-Query-Matcher wird ausgeführt, um zu prüfen, ob das aktualisierte Dokument mit einem der derzeit registrierten Snapshot-Listener übereinstimmt. In diesem Beispiel stimmt das Dokument mit dem Snapshot-Listener von Client B überein. Wie der Name schon sagt, können Sie sich den Reverse-Query-Matcher als normale Datenbankabfrage vorstellen, die jedoch umgekehrt ausgeführt wird. Anstatt Dokumente zu durchsuchen, um diejenigen zu finden, die mit einer Abfrage übereinstimmen, werden effizient Abfragen durchsucht, um diejenigen zu finden, die mit einem eingehenden Dokument übereinstimmen. Wenn eine Übereinstimmung gefunden wird, leitet das System das betreffende Dokument an die Snapshot-Listener weiter. Anschließend wertet das System die Firebase-Sicherheitsregeln der Datenbank aus, um sicherzustellen, dass nur autorisierte Nutzer die Daten erhalten.
- Das System leitet die Dokumentaktualisierung an das SDK auf dem Gerät von Client B weiter und der
onSnapshot-Callback wird ausgelöst. Wenn die lokale Persistenz aktiviert ist, wendet das SDK die Aktualisierung auch auf den lokalen Cache an.
Ein wichtiger Teil der Skalierbarkeit von Cloud Firestore's hängt vom Fan-Out vom Änderungsprotokoll an die Abo-Handler und die Frontend-Server ab. Durch das Fan-Out kann eine einzelne Datenänderung effizient weitergegeben werden, um Millionen von Echtzeitabfragen und verbundenen Nutzern zu bedienen. Durch die Ausführung vieler Replikate all dieser Komponenten in mehreren Zonen (oder mehreren Regionen bei einer multiregionalen Bereitstellung) erreicht Cloud Firestore eine Hochverfügbarkeit und Skalierbarkeit.
Alle Lesevorgänge, die von mobilen und Web-SDKs ausgegeben werden, folgen dem oben beschriebenen Modell. Sie führen eine Polling-Abfrage gefolgt vom Listen-Modus aus, um die Konsistenzgarantien aufrechtzuerhalten. Dies gilt auch für Echtzeit-Listener, Aufrufe zum Abrufen eines Dokuments und einmalige Abfragen. Sie können sich das Abrufen einzelner Dokumente und einmalige Abfragen als kurzlebige Snapshot-Listener vorstellen, die ähnliche Einschränkungen hinsichtlich der Leistung aufweisen.
Best Practices zum Skalieren von Echtzeitabfragen anwenden
Wenden Sie die folgenden Best Practices an, um skalierbare Echtzeitabfragen zu entwerfen.
Hoher Schreibtraffic im System
In diesem Abschnitt erfahren Sie, wie das System auf eine steigende Anzahl von Schreibanfragen reagiert.
Die Cloud Firestore Änderungsprotokolle, die die Echtzeitabfragen automatisch horizontal skaliert werden, wenn der Schreibtraffic zunimmt. Wenn die Schreibgeschwindigkeit für eine Datenbank über das hinausgeht, was ein einzelner Server verarbeiten kann, wird das Änderungsprotokoll auf mehrere Server aufgeteilt und die Abfrageverarbeitung verwendet Daten von mehreren Abo-Handlern anstelle von einem. Aus Sicht des Clients und des SDK ist dies alles transparent und es sind keine Maßnahmen von der App erforderlich, wenn Splits auftreten. Das folgende Diagramm zeigt, wie Echtzeitabfragen skaliert werden:
Durch die automatische Skalierung können Sie den Schreibtraffic unbegrenzt erhöhen. Wenn der Traffic jedoch zunimmt, kann es einige Zeit dauern, bis das System reagiert. Folgen Sie den Empfehlungen der 5-5-5-Regel, um keinen Schreib-Hotspot zu erstellen. Key Visualizer ist ein nützliches Tool zum Analysieren von Schreib-Hotspots.
Viele Apps haben ein vorhersehbares organisches Wachstum, das Cloud Firestore ohne Vorsichtsmaßnahmen bewältigen kann. Batcharbeitslasten wie das Importieren eines großen Datasets können jedoch zu schnell zu mehr Schreibvorgängen führen. Achten Sie beim Entwerfen Ihrer App darauf, woher der Schreibtraffic kommt.
Zusammenspiel von Schreib- und Lesevorgängen
Sie können sich das Echtzeit-Abfragesystem als Pipeline vorstellen, die Schreibvorgänge mit Lesern verbindet. Jedes Mal, wenn ein Dokument erstellt, aktualisiert oder gelöscht wird, wird die Änderung vom Speichersystem an die derzeit registrierten Listener weitergegeben. Cloud Firestore Änderungsprotokollstruktur garantiert eine starke Konsistenz. Das bedeutet, dass Ihre App niemals Benachrichtigungen über Aktualisierungen erhält, die im Vergleich zu dem Zeitpunkt, an dem die Datenbank die Daten Änderungen übernommen hat, in der falschen Reihenfolge sind. Dies vereinfacht die App-Entwicklung, da Grenzfälle im Zusammenhang mit der Datenkonsistenz vermieden werden.
Diese verbundene Pipeline bedeutet, dass ein Schreibvorgang, der Hotspots oder Sperrkonflikte verursacht, sich negativ auf Lesevorgänge auswirken kann. Wenn Schreibvorgänge fehlschlagen oder gedrosselt werden, kann ein Lesevorgang warten, bis konsistente Daten aus dem Änderungsprotokoll verfügbar sind. Wenn dies in Ihrer App passiert, sehen Sie möglicherweise sowohl langsame Schreibvorgänge als auch korrelierte langsame Antwortzeiten für Abfragen. Hotspots zu vermeiden ist der Schlüssel, um dieses Problem zu umgehen.
Dokumente und Schreibvorgänge klein halten
Wenn Sie Apps mit Snapshot-Listenern erstellen, möchten Sie in der Regel, dass Nutzer schnell über Datenänderungen informiert werden. Halten Sie die Dinge dazu möglichst klein. Das System kann kleine Dokumente mit Dutzenden von Feldern sehr schnell verarbeiten. Die Verarbeitung größerer Dokumente mit Hunderten von Feldern und großen Datenmengen dauert länger.
Ebenso sollten Sie kurze, schnelle Commit- und Schreibvorgänge bevorzugen, um die Latenz niedrig zu halten. Große Batches können aus Sicht des Schreibers einen höheren Durchsatz ermöglichen, aber die Benachrichtigungszeit für Snapshot-Listener tatsächlich verlängern. Das ist oft kontraintuitiv im Vergleich zu anderen Datenbanksystemen, bei denen Sie Batching verwenden können, um die Leistung zu verbessern.
Effiziente Listener verwenden
Wenn die Schreibgeschwindigkeit für Ihre Datenbank zunimmt, Cloud Firestore verteilt die Datenverarbeitung auf viele Server. Der Sharding-Algorithmus von Cloud Firestore versucht, Daten aus derselben Sammlung oder Sammlungsgruppe auf demselben Änderungsprotokollserver zu platzieren. Das System versucht, den möglichen Schreibdurchsatz zu maximieren und gleichzeitig die Anzahl der Server, die an der Verarbeitung einer Abfrage beteiligt sind, so niedrig wie möglich zu halten.
Bestimmte Muster können jedoch immer noch zu suboptimalem Verhalten für Snapshot-Listener führen. Wenn Ihre App beispielsweise die meisten Daten in einer großen Sammlung speichert, muss der Listener möglicherweise eine Verbindung zu vielen Servern herstellen, um alle erforderlichen Daten zu erhalten. Das gilt auch dann, wenn Sie einen Abfragefilter anwenden. Die Verbindung zu vielen Servern erhöht das Risiko langsamerer Antworten.
Um diese langsameren Antworten zu vermeiden, entwerfen Sie Ihr Schema und Ihre App so, dass das System Listener bedienen kann, ohne viele verschiedene Server zu verwenden. Es ist möglicherweise am besten, Ihre Daten in kleinere Sammlungen mit niedrigeren Schreibgeschwindigkeiten aufzuteilen.
Das ist ähnlich wie bei Leistungsabfragen in einer relationalen Datenbank, die vollständige Tabellenscans erfordern. In einer relationalen Datenbank entspricht eine Abfrage, die einen vollständigen Tabellenscan erfordert, einem Snapshot-Listener, der eine Sammlung mit hoher Änderungsrate überwacht. Die Leistung ist möglicherweise geringer als bei einer Abfrage, die die Datenbank mit einem spezifischeren Index bedienen kann. Eine Abfrage mit einem spezifischeren Index ist wie ein Snapshot-Listener, der ein einzelnes Dokument oder eine Sammlung überwacht, die sich seltener ändert. Sie sollten Ihre App einem Lasttest unterziehen, um das Verhalten und die Anforderungen Ihres Anwendungsfalls besser zu verstehen.
Polling-Abfragen schnell halten
Ein weiterer wichtiger Aspekt reaktionsschneller Echtzeitabfragen besteht darin, dass die Polling-Abfrage zum Bootstrappen der Daten schnell und effizient ist. Wenn sich ein neuer Snapshot-Listener zum ersten Mal verbindet, muss er das gesamte Ergebnisset laden und an das Gerät des Nutzers senden. Langsame Abfragen machen Ihre App weniger reaktionsschnell. Dazu gehören beispielsweise Abfragen, die versuchen, viele Dokumente zu lesen, oder Abfragen, die nicht die entsprechenden Indexe verwenden.
Unter bestimmten Umständen kann ein Listener auch vom Listen- in den Polling-Modus zurückkehren. Das geschieht automatisch und ist für die SDKs und Ihre App transparent. Die folgenden Bedingungen können einen Polling-Modus auslösen:
- Das System gleicht ein Änderungsprotokoll aufgrund von Änderungen der Last neu aus.
- Hotspots verursachen fehlgeschlagene oder verzögerte Schreibvorgänge in der Datenbank.
- Vorübergehende Serverneustarts wirken sich vorübergehend auf Listener aus.
Wenn Ihre Polling-Abfragen schnell genug sind, ist ein Polling-Modus für die Nutzer Ihrer App transparent.
Langlebige Listener bevorzugen
Das Öffnen und Aufrechterhalten von Listenern so lange wie möglich ist oft die kostengünstigste Möglichkeit, eine App zu erstellen, die Cloud Firestore verwendet. Bei der Verwendung von Cloud Firestore werden Ihnen die an Ihre App zurückgegebenen Dokumente in Rechnung gestellt, nicht die Aufrechterhaltung einer offenen Verbindung. Ein langlebiger Snapshot-Listener liest während seiner Lebensdauer nur die Daten, die zum Bedienen der Abfrage erforderlich sind. Dazu gehört ein anfänglicher Polling-Vorgang, gefolgt von Benachrichtigungen, wenn sich die Daten tatsächlich ändern. Einmalige Abfragen lesen dagegen Daten noch einmal, die sich seit der letzten Ausführung der Abfrage durch die App möglicherweise nicht geändert haben.
In Fällen, in denen Ihre App eine hohe Datenrate verarbeiten muss, sind Snapshot-Listener möglicherweise nicht geeignet. Wenn Ihr Anwendungsfall beispielsweise über einen längeren Zeitraum viele Dokumente pro Sekunde über eine Verbindung überträgt, ist es möglicherweise besser, einmalige Abfragen zu verwenden, die seltener ausgeführt werden.
Weitere Informationen
- Erfahren Sie, wie Sie Snapshot-Listener verwenden.
- Weitere Best Practices