In diesem Dokument erfahren Sie, wie Sie Ihre serverlose Anwendung auf Tausende von Vorgängen pro Sekunde oder Hunderttausende von gleichzeitigen Nutzern skalieren können. Dieses Dokument enthält weiterführende Themen zum besseren Verständnis des Systems. Wenn Sie gerade erst mit Cloud Firestore beginnen, lesen Sie stattdessen die Kurzanleitung.
Cloud Firestore und die Firebase Mobile/Web SDKs 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 Aktualisierungen der Daten überwachen. Mit Echtzeitaktualisierungen können Sie responsive Apps erstellen, für die keine Serverinfrastruktur erforderlich ist. Es ist zwar sehr einfach, etwas in Betrieb zu nehmen, aber es ist hilfreich, die Einschränkungen der Systeme zu kennen, aus denen Cloud Firestore besteht, damit Ihre serverlose App bei steigendem Traffic skaliert und eine gute Leistung erbringt.
In den folgenden Abschnitten finden Sie Tipps zur Skalierung Ihrer App.
Speicherort der Datenbank in der Nähe Ihrer Nutzer auswählen
Das folgende Diagramm zeigt die Architektur einer Echtzeit-App:
Wenn eine Anwendung, die auf dem Gerät eines Nutzers (Mobilgerät oder Web) ausgeführt wird, eine Verbindung zu Cloud Firestore herstellt, wird die Verbindung an einen Cloud Firestore-Front-End-Server in derselben Region wie die Datenbank weitergeleitet. Wenn sich Ihre Datenbank beispielsweise in us-east1
befindet, geht die Verbindung auch zu einem Cloud Firestore-Front-End, das sich ebenfalls in us-east1
befindet. Diese Verbindungen sind langlebig und bleiben offen, 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 Datenbankstandort Cloud Firestore wirkt sich auf die Latenz des Nutzers aus. Ein Nutzer in Indien, dessen App mit einer Datenbank in einer Google Cloud-Region in Nordamerika kommuniziert, könnte beispielsweise feststellen, dass die App langsamer und weniger reaktionsschnell ist 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 wirken sich auf die Zuverlässigkeit Ihrer App aus:
Offlinemodus aktivieren
Die Firebase SDKs bieten eine Offlinedatenpersistenz. Wenn die App auf dem Gerät des Nutzers keine Verbindung zu Cloud Firestore herstellen kann, bleibt sie dank der lokal im Cache gespeicherten Daten weiterhin nutzbar. So ist der Datenzugriff auch dann gewährleistet, wenn Nutzer eine schwankende 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 sorgen für Wiederholungsversuche und die Wiederherstellung unterbrochener Verbindungen. So lassen sich vorübergehende Fehler vermeiden, 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 gibt es mehrere Abwägungen. Der Hauptunterschied besteht darin, wie Daten repliziert werden. Dadurch werden die Verfügbarkeitsgarantien Ihrer App erhöht. Eine multiregionale Instanz bietet eine höhere Zuverlässigkeit beim Bereitstellen und erhöht die Langlebigkeit Ihrer Daten. Der Nachteil sind jedoch die Kosten.
Echtzeitabfragesystem
Mit Echtzeitabfragen, auch Snapshot-Listener genannt, kann die Anwendung Änderungen in der Datenbank beobachten und Benachrichtigungen mit niedriger Latenz erhalten, sobald sich die Daten ändern. Eine Anwendung kann dasselbe Ergebnis erzielen, wenn sie die Datenbank regelmäßig nach Aktualisierungen abfragt. Dies ist jedoch oft langsamer, teurer und erfordert mehr Code. Beispiele zum Einrichten und Verwenden von Echtzeitabfragen finden Sie unter Echtzeitaktualisierungen abrufen. In den folgenden Abschnitten erfahren Sie mehr über die Funktionsweise von Snapshot-Listenern und einige Best Practices für die Skalierung von Echtzeitabfragen bei gleichzeitiger Beibehaltung der Leistung.
Angenommen, zwei Nutzer verbinden sich über eine Messaging-App, die mit einem der mobilen SDKs erstellt wurde, mit Cloud Firestore.
Client A schreibt in die Datenbank, um Dokumente in einer Sammlung mit dem Namen 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. Kunde B erhält sofort eine Benachrichtigung, wenn jemand eine neue Nachricht erstellt. Das folgende Diagramm zeigt die Architektur eines Snapshot-Empfängers:
Wenn Client B einen Snapshot-Listener mit der Datenbank verbindet, geschieht Folgendes:
- 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 den Datensatz zu initialisieren. Es wird die gesamte Ergebnismenge der übereinstimmenden Dokumente geladen. Dies wird als Abfrage bezeichnet. Das System wertet dann die Firebase-Sicherheitsregeln der Datenbank 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 Liefermodus. 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 überträgt die Dokumentänderung an das Speichersystem.
- Transaktional speichert das System dieselbe Aktualisierung in einem internen Änderungslog. Das Änderungsprotokoll legt eine strikte Reihenfolge der Änderungen fest, sobald sie auftreten.
- Das Änderungs-Log verteilt die aktualisierten Daten an einen Pool von Abo-Handlern.
- Ein Reverse-Query-Matcher wird ausgeführt, um zu prüfen, ob das aktualisierte Dokument mit derzeit registrierten Snapshot-Listenern übereinstimmt. In diesem Beispiel entspricht das Dokument dem Snapshot-Listener von Client B. Wie der Name schon sagt, können Sie sich den umgekehrten Abfrageabgleich als normale Datenbankabfrage vorstellen, die aber in umgekehrter Reihenfolge ausgeführt wird. Anstatt nach Dokumenten zu suchen, die mit einer Suchanfrage übereinstimmen, werden effizient Suchanfragen nach solchen gesucht, die mit einem eingehenden Dokument übereinstimmen. Wenn eine Übereinstimmung gefunden wird, leitet das System das betreffende Dokument an die Snapshot-Listener weiter. Anschließend prüft das System die Firebase-Sicherheitsregeln der Datenbank, 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 hängt vom Fan-Out vom Änderungslog an die Abo-Handler und die Front-End-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 Repliken all dieser Komponenten in mehreren Zonen (oder mehreren Regionen bei einer Bereitstellung mit mehreren Regionen) wird bei Cloud Firestore eine hohe Verfügbarkeit und Skalierbarkeit erreicht.
Alle Lesevorgänge von mobilen und Web-SDKs folgen dem obigen Modell. Sie führen eine Abfrage aus, gefolgt vom Listenermodus, um Konsistenzgarantien aufrechtzuerhalten. Dies gilt auch für Echtzeit-Listener, Aufrufe zum Abrufen eines Dokuments und Einmalabfragen. Sie können sich die Abrufvorgänge einzelner Dokumente und einmalige Abfragen als kurzlebige Snapshot-Listener vorstellen, die ähnliche Leistungseinschränkungen haben.
Best Practices für die Skalierung von Echtzeitabfragen anwenden
Wenden Sie die folgenden Best Practices an, um skalierbare Echtzeitabfragen zu entwerfen.
Hohe Anzahl von Schreibvorgängen im System verstehen
In diesem Abschnitt erfahren Sie, wie das System auf eine steigende Anzahl von Schreibanfragen reagiert.
Die Cloud Firestore-Änderungslisten, die die Echtzeitabfragen steuern, werden automatisch horizontal skaliert, wenn der Schreibverkehr zunimmt. Wenn die Schreibrate für eine Datenbank über die Kapazität eines einzelnen Servers hinausgeht, wird der Änderungsverlauf auf mehrere Server aufgeteilt und die Abfrageverarbeitung beginnt, Daten von mehreren Abo-Handlern anstelle von einem zu verwenden. Aus Sicht des Clients und des SDKs ist das alles transparent und es sind keine Maßnahmen erforderlich, wenn es zu Aufteilungen kommt. Das folgende Diagramm zeigt, wie Echtzeitabfragen skaliert werden:
Mit der automatischen Skalierung können Sie den Schreibtraffic unbegrenzt erhöhen. Wenn dieser jedoch zunimmt, kann es eine Weile dauern, bis das System reagiert. Folgen Sie den Empfehlungen der 5-5-5-Regel, um Hotspots zu vermeiden. Key Visualizer ist ein nützliches Tool zur Analyse von Schreib-Hotspots.
Viele Apps verzeichnen ein vorhersehbares organisches Wachstum, das Cloud Firestore ohne Vorkehrungen bewältigen kann. Bei Batch-Arbeitslasten wie dem Importieren eines großen Datensatzes können Schreibvorgänge jedoch zu schnell ansteigen. Berücksichtigen Sie beim Entwerfen Ihrer App, woher die Schreibzugriffe stammen.
Verstehen, wie Schreib- und Lesevorgänge interagieren
Sie können sich das Echtzeit-Abfragesystem als eine 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. Die Änderungsliste von Cloud Firestore sorgt für eine hohe Konsistenz. Das bedeutet, dass Ihre App nie Benachrichtigungen zu Updates erhält, die nicht der Reihenfolge entsprechen, in der die Datenänderungen in der Datenbank vorgenommen wurden. Dies vereinfacht die Anwendungsentwicklung, da Grenzfälle bezüglich der Datenkonsistenz beseitigt 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 ins Stocken geraten, weil auf konsistente Daten aus dem Änderungsprotokoll gewartet wird. Wenn dies in Ihrer App der Fall ist, kann es sowohl zu langsamen Schreibvorgängen als auch zu entsprechenden langen Reaktionszeiten bei Abfragen kommen. Hotspots sind der Schlüssel, um dieses Problem zu vermeiden.
Dokumente und Schreibvorgänge klein halten
Wenn Sie Anwendungen mit Snapshot-Listenern erstellen, möchten Sie in der Regel, dass Nutzer schnell Informationen zu Datenänderungen erhalten. Versuchen Sie, die Größe möglichst klein zu halten. Das System kann kleine Dokumente mit Dutzenden von Feldern sehr schnell durch das System verschieben. Größere Dokumente mit Hunderten von Feldern und großen Datenmengen dauern länger.
Gleichermaßen sollten Sie kurze, schnelle Commit- und Schreibvorgänge bevorzugen, um die Latenz niedrig zu halten. Große Batches können zwar aus Sicht des Schreibers zu einem höheren Durchsatz führen, aber die Benachrichtigungszeit für Snapshot-Listener verlängern. Das ist oft kontraintuitiv im Vergleich zu anderen Datenbanksystemen, bei denen Sie die Leistung mithilfe von Batchverarbeitungen verbessern können.
Effizientere Listener verwenden
Wenn die Schreibraten für Ihre Datenbank steigen, teilt Cloud Firestore die Datenverarbeitung auf viele Server auf. Der Fragmentierungsalgorithmus von Cloud Firestore versucht, Daten aus derselben Sammlung oder Sammlungsgruppe auf demselben Änderungslogserver zu speichern. Das System versucht, den möglichen Schreibdurchsatz zu maximieren und gleichzeitig die Anzahl der Server, die an der Verarbeitung einer Abfrage beteiligt sind, so gering wie möglich zu halten.
Bestimmte Muster können jedoch trotzdem zu einem nicht optimalen 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 benötigten Daten zu erhalten. Das gilt auch dann, wenn Sie einen Abfragefilter anwenden. Wenn Sie eine Verbindung zu vielen Servern herstellen, erhöht sich das Risiko langsamerer Antworten.
Um diese langsameren Antworten zu vermeiden, solltest du dein Schema und deine App so gestalten, dass das System Hörer bedienen kann, ohne viele verschiedene Server aufzurufen. Unter Umständen empfiehlt es sich, die Daten in kleinere Sammlungen mit geringeren Schreibraten aufzuteilen.
Das ist vergleichbar mit Leistungsabfragen in einer relationalen Datenbank, die einen vollständigen Tabellenscan erfordern. In einer relationalen Datenbank entspricht eine Abfrage, die einen vollständigen Tabellenscan erfordert, einem Snapshot-Listener, der eine Sammlung mit hoher Abwanderung überwacht. Im Vergleich zu einer Abfrage, die die Datenbank mit einem spezifischeren Index verarbeiten kann, ist sie möglicherweise langsam. Eine Abfrage mit einem spezifischeren Index ist wie ein Snapshot-Listener, der ein einzelnes Dokument oder eine Sammlung beobachtet, die sich seltener ändert. Sie sollten einen Lasttest für Ihre App ausführen, um das Verhalten und die Anforderungen Ihres Anwendungsfalls besser zu verstehen.
Schnelle Abfragen beibehalten
Ein weiterer wichtiger Aspekt bei responsiven Echtzeitabfragen besteht darin, dafür zu sorgen, dass die Abfrage zum Bootstrapping der Daten schnell und effizient ist. Wenn ein neuer Snapshot-Listener zum ersten Mal eine Verbindung herstellt, muss er die gesamte Ergebnismenge laden und an das Gerät des Nutzers senden. Langsame Abfragen verlangsamen Ihre App. Dazu gehören beispielsweise Abfragen, bei denen versucht wird, viele Dokumente zu lesen, oder Abfragen, bei denen die entsprechenden Indexe nicht verwendet werden.
Unter bestimmten Umständen kann ein Listener auch von einem Überwachungsstatus zu einem Abfragestatus zurückkehren. Dies geschieht automatisch und ist für die SDKs und Ihre App transparent. Die folgenden Bedingungen können einen Abfragestatus auslösen:
- Das System gleicht einen Änderungslog aufgrund von Laständerungen neu aus.
- Hotspots führen zu fehlgeschlagenen oder verzögerten Schreibvorgängen in der Datenbank.
- Vorübergehende Serverneustarts wirken sich vorübergehend auf Listener aus.
Wenn Ihre Abfrageabfragen schnell genug sind, wird der Abfragestatus für die Nutzer Ihrer Anwendung transparent.
Langlebige Listener bevorzugen
Das Öffnen und so lange wie möglich Aufrechterhalten von Listenern ist oft die kostengünstigste Methode, eine App zu entwickeln, die Cloud Firestore verwendet. Wenn Sie Cloud Firestore verwenden, werden Ihnen die Dokumente in Rechnung gestellt, die an Ihre App zurückgegeben werden, und nicht die Aufrechterhaltung einer offenen Verbindung. Ein langlebiger Snapshot-Listener liest nur die Daten, die er zur Verarbeitung der Abfrage während seiner gesamten Lebensdauer benötigt. Dazu gehört ein erster Abfragevorgang, auf den Benachrichtigungen folgen, wenn sich die Daten tatsächlich ändern. Bei einmaligen Abfragen werden dagegen Daten noch einmal gelesen, die sich möglicherweise nicht geändert haben, seit die App die Abfrage zuletzt ausgeführt hat.
Wenn Ihre App eine hohe Datenrate verbrauchen muss, sind Snapshot-Listener möglicherweise nicht geeignet. Wenn bei Ihrem Anwendungsfall beispielsweise über einen längeren Zeitraum viele Dokumente pro Sekunde über eine Verbindung gesendet werden, sollten Sie besser einmalige Abfragen mit einer geringeren Häufigkeit verwenden.