Best Practices

Die hier aufgeführten Best Practices bieten eine Kurzreferenz, wenn Sie eine Anwendung erstellen, die Firestore verwendet.

Datenbankspeicherort

Wählen Sie beim Erstellen Ihrer Datenbankinstanz den Datenbankspeicherort aus, der Ihren Nutzern und Rechenressourcen am nächsten ist. Weit reichende Netzwerk-Hops sind fehleranfälliger und erhöhen die Abfragelatenz.

Wählen Sie zum Maximieren der Verfügbarkeit und Langlebigkeit Ihrer Anwendung einen Standort mit mehreren Regionen aus und platzieren Sie wichtige Rechenressourcen in mindestens zwei Regionen.

Wählen Sie einen regionalen Standort aus, um die Kosten niedrig zu halten, um eine niedrige Schreiblatenz zu erzielen, wenn Ihre Anwendung empfindlich auf Latenz reagiert, oder um die Datenbank gemeinsam mit anderen GCP-Ressourcen am selben Standort zu speichern.

Dokument-IDs

  • Vermeiden Sie die Dokument-IDs . und ...
  • Verwenden Sie in Dokument-IDs keine Schrägstriche (/).
  • Verwenden Sie keine kontinuierlich ansteigenden Dokument-IDs, z. B.:

    • Customer1, Customer2, Customer3, ...
    • Product 1, Product 2, Product 3, ...

    Solche aufeinanderfolgenden IDs können zum Heißlaufen führen, was sich auf die Latenz auswirkt.

Feldnamen

  • Vermeiden Sie die folgenden Zeichen in Feldnamen, da sie zusätzliche Escapezeichen erfordern:

    • Punkt (.)
    • Eckige Klammer links ([)
    • Eckige Klammer rechts (])
    • Sternchen (*)
    • Backtick (`)

Indexe

Schreiblatenz reduzieren

Der Hauptgrund für die Schreiblatenz ist die Indexverzweigung. Best Practices zur Reduzierung der Indexverzweigung:

  • Legen Sie Indexausnahmen auf Sammlungsebene fest. Deaktivieren Sie einfach die absteigende Sortierung und die Array-Indexierung. Durch das Entfernen nicht verwendeter indexierter Werte sinken auch die Speicherkosten.

  • Reduzieren Sie die Anzahl der Dokumente in einer Transaktion. Wenn Sie eine große Anzahl von Dokumenten schreiben möchten, sollten Sie stattdessen einen Bulk-Writer anstelle des atomaren Batch-Writers verwenden.

Indexausnahmen

Bei den meisten Anwendungen sind die automatische Indexierung sowie Fehlermeldungslinks zur Verwaltung der Indexe ausreichend. In den folgenden Fällen können Sie aber Ausnahmen für einzelne Felder hinzufügen:

Fall Beschreibung
Große Stringfelder

Wenn Sie ein Stringfeld haben, das oft lange Stringwerte enthält, die Sie nicht für Abfragen verwenden, können Sie die Speicherkosten senken, indem Sie das Feld von der Indexierung ausnehmen.

Hohe Schreibraten für eine Sammlung, die Dokumente mit aufeinanderfolgenden Werten enthält

Wenn Sie ein Feld indexieren, dessen Werte über die Dokumente einer Sammlung hinweg kontinuierlich zu- oder abnehmen, z. B. im Fall eines Zeitstempels, beträgt die maximale Schreibrate für die Sammlung 500 Schreibvorgänge pro Sekunde. Wenn Sie keine Abfrage basierend auf dem Feld mit aufeinanderfolgenden Werten durchführen, können Sie das Feld von der Indexierung ausnehmen, um dieses Limit zu umgehen.

In einem IoT-Anwendungsfall mit einer hohen Schreibrate kann sich beispielsweise eine Sammlung, die Dokumente mit einem Zeitstempelfeld enthält, dem Limit von 500 Schreibvorgängen pro Sekunde nähern.

TTL-Felder

Wenn Sie TTL-Richtlinien (Gültigkeitsdauer) verwenden, muss das TTL-Feld ein Zeitstempel sein. Die Indexierung von TTL-Feldern ist standardmäßig aktiviert und kann sich bei höheren Zugriffsraten auf die Leistung auswirken. Es empfiehlt sich, für Ihre TTL-Felder Ausnahmen für einzelne Felder hinzuzufügen.

Große Array- oder Map-Felder

Große Array- oder Kartenfelder können sich dem Limit von 40.000 Indexeinträgen pro Dokument nähern. Wenn Sie keine Abfragen basierend auf einem großen Array- oder Kartenfeld durchführen, sollten Sie dieses von der Indexierung ausnehmen.

Lese- und Schreibvorgänge

  • Die genaue maximale Rate, mit der eine App ein einzelnes Dokument aktualisieren kann, hängt stark von der Arbeitslast ab. Weitere Informationen finden Sie unter Aktualisierungen für ein einzelnes Dokument.

  • Verwenden Sie sofern verfügbar asynchrone Aufrufe anstelle von synchronen Aufrufen. Asynchrone Aufrufe minimieren die Auswirkung auf die Latenz. Angenommen, eine Anwendung benötigt das Ergebnis eines Dokument-Lookups sowie die Ergebnisse einer Abfrage, bevor eine Antwort gerendert wird. Wenn der Lookup und die Abfrage keine Datenabhängigkeit haben, muss nicht synchron auf den Abschluss des Lookups gewartet werden, bevor die Abfrage initiiert wird.

  • Verwenden Sie keine Offsets. Verwenden Sie stattdessen Cursors. Wenn Sie einen Offset verwenden, werden die übersprungenen Dokumente nicht an Ihre Anwendung zurückgegeben, werden aber intern abgerufen. Die übersprungenen Dokumente wirken sich auf die Latenz der Abfrage aus und Ihrer Anwendung werden die zum Abrufen erforderlichen Lesevorgänge in Rechnung gestellt.

Wiederholungsversuche für Transaktionen

Die SDKs und Clientbibliotheken von Firestore wiederholen fehlgeschlagene Transaktionen automatisch, um vorübergehende Fehler zu beheben. Wenn Ihre Anwendung direkt über die REST API oder die RPC API statt über ein SDK auf Firestore zugreift, sollte Ihre Anwendung Transaktionswiederholungen ausführen. Dies erhöht die Zuverlässigkeit.

Echtzeitaktualisierungen

Best Practices für Echtzeitaktualisierungen finden Sie unter Echtzeitabfragen im großen Maßstab.

Skalierbares Programmdesign

Die folgenden Best Practices beschreiben, wie Situationen vermieden werden können, die zu Konflikten führen.

Aktualisierungen für ein einzelnes Dokument

Überlegen Sie beim Entwerfen Ihrer App, wie schnell einzelne Dokumente aktualisiert werden sollen. Die Leistung Ihrer Arbeitslast lässt sich am besten mithilfe von Lasttests charakterisieren. Die genaue maximale Rate, mit der eine App ein einzelnes Dokument aktualisieren kann, hängt stark von der Arbeitslast ab. Zu den Faktoren gehören die Schreibrate, die Konkurrenz zwischen Anfragen und die Anzahl der betroffenen Indexe.

Bei einem Schreibvorgang für ein Dokument werden das Dokument und alle zugehörigen Indexe aktualisiert. Firestore wendet den Schreibvorgang synchron auf ein Quorum von Replicas an. Bei einer ausreichend hohen Schreibrate kommt es in der Datenbank zu Konflikten, einer höheren Latenz oder anderen Fehlern.

Hoher Lese-, Schreib- und Löschraten für einen kleinen Dokumentbereich

Vermeiden Sie hohe Lese- oder Schreibraten bei Dokumenten, die lexikografisch eng beieinanderliegen. Andernfalls treten bei Ihrer Anwendung Konfliktfehler auf. Dieses Problem wird als Heißlaufen bezeichnet. Dazu kann es bei Ihrer Anwendung kommen, wenn sie eine der folgenden Aktionen ausführt:

  • Sie erstellt neue Dokumente mit einer sehr hohen Rate und ordnet ihre eigenen monoton ansteigenden IDs zu.

    Firestore ordnet Dokument-IDs mithilfe eines Streuungsalgorithmus zu. Wenn Sie neue Dokumente mit automatischen Dokument-IDs erstellen, sollte es bei Schreibvorgängen nicht zu einem Heißlaufen kommen.

  • Sie erstellt neue Dokumente in einer Sammlung mit wenigen Dokumenten mit einer hohen Rate.

  • Sie erstellt mit einer sehr hohen Rate neue Dokumente mit einem kontinuierlich zunehmenden Feld, z. B. einem Zeitstempel.

  • Sie löscht Dokumente in einer Sammlung mit einer hohen Rate.

  • Sie schreibt mit einer sehr hohen Rate in die Datenbank, ohne den Traffic nach und nach zu erhöhen.

Löschte Daten nicht überspringen

Vermeiden Sie Abfragen, bei denen kürzlich gelöschte Daten übersprungen werden. Bei einer Abfrage müssen möglicherweise eine große Anzahl von Indexeinträgen übersprungen werden, wenn die ersten Abfrageergebnisse vor Kurzem gelöscht wurden.

Ein Beispiel für eine Arbeitslast, bei der möglicherweise viele gelöschte Daten übersprungen werden müssen, ist eine, bei der versucht wird, die ältesten Arbeitselemente in der Warteschlange zu finden. Die Abfrage könnte so aussehen:

docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
delete_batch.commit()

Bei jeder Ausführung dieser Abfrage werden die Indexeinträge für das Feld created in allen kürzlich gelöschten Dokumenten gescannt. Das verlangsamt die Abfragen.

Verwenden Sie die Methode start_at, um den besten Ausgangspunkt für die Leistungsverbesserung zu finden. Beispiel:

completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
    {'created': completed_items.get('last_completed')}).order_by(
        'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
  last_completed = doc.get('created')

if last_completed:
  delete_batch.update(completed_items.reference,
                      {'last_completed': last_completed})
  delete_batch.commit()

HINWEIS: Im Beispiel oben wird ein monoton steigendes Feld verwendet, was bei hohen Schreibraten ein Antimuster ist.

Traffic erhöhen

Sie sollten den Traffic zu neuen Sammlungen oder lexikografisch eng beieinanderliegenden Dokumenten schrittweise erhöhen, damit Firestore genügend Zeit hat, Dokumente auf den erhöhten Traffic vorzubereiten. Wir empfehlen, bei einer neuen Sammlung mit maximal 500 Vorgängen pro Sekunde zu beginnen und den Traffic dann alle 5 Minuten um 50 % zu erhöhen. Auf ähnliche Weise lässt sich auch der Schreibtraffic erhöhen. Beachten Sie dabei aber die Standardlimits von Firestore. Achten Sie darauf, dass die Vorgänge relativ gleichmäßig über den Schlüsselbereich verteilt sind. Dies wird als "500/50/5"-Regel bezeichnet.

Traffic zu einer neuen Sammlung migrieren

Die schrittweise Erhöhung ist besonders wichtig, wenn Sie den Anwendungs-Traffic von einer Sammlung zu einer anderen migrieren. Eine einfache Möglichkeit zur Umsetzung dieser Migration besteht darin, aus der alten Sammlung zu lesen. Wenn das Dokument nicht vorhanden ist, lesen Sie aus der neuen Sammlung. Dies kann aber zu einem plötzlichen Anstieg des Traffics zu lexikografisch eng beieinanderliegenden Dokumenten in der neuen Sammlung führen. Firestore ist möglicherweise nicht in der Lage, die neue Sammlung effizient auf erhöhten Traffic vorzubereiten, insbesondere wenn sie nur wenige Dokumente enthält.

Ein ähnliches Problem kann auftreten, wenn Sie die Dokument-IDs vieler Dokumente innerhalb einer Sammlung ändern.

Die beste Strategie für die Migration des Traffics zu einer neuen Sammlung hängt von Ihrem Datenmodell ab. Unten sehen Sie eine Beispielstrategie, die als parallele Lesevorgänge bezeichnet wird. Sie müssen prüfen, ob diese Strategie für Ihre Daten effektiv ist. Ein wichtiger Aspekt sind die Kosten paralleler Vorgänge während der Migration.

Parallele Lesevorgänge

Um für das Migrieren des Traffics zu einer neuen Sammlung parallele Lesevorgänge zu implementieren, lesen Sie zuerst aus der alten Sammlung. Wenn das Dokument fehlt, lesen Sie aus der neuen Sammlung. Eine hohe Rate von Lesevorgängen in nicht vorhandenen Dokumenten kann zum Heißlaufen führen. Achten Sie daher darauf, dass die Last für die neue Sammlung nur allmählich erhöht wird. Eine bessere Strategie besteht darin, das alte Dokument in die neue Sammlung zu kopieren und dann zu löschen. Erhöhen Sie parallele Lesevorgänge schrittweise, um zu gewährleisten, dass Firestore den Traffic zur neuen Sammlung verarbeiten kann.

Ein mögliches Vorgehen zur schrittweisen Steigerung von Lese- oder Schreibvorgängen für eine neue Sammlung ist die Verwendung eines deterministischen Hash-Werts der Nutzer-ID. Damit kann per Zufallsprinzip ein Teil der Nutzer ausgewählt werden, die versuchen, in neue Dokumente zu schreiben. Achten Sie darauf, dass das Ergebnis des Nutzer-ID-Hash-Werts weder durch die Funktion noch durch das Nutzerverhalten verzerrt wird.

Führen Sie einen Batchjob aus, durch den alle Ihre Daten aus den alten Dokumenten in die neue Sammlung kopiert werden. Der Batchjob sollte Schreibvorgänge für aufeinanderfolgende Dokument-IDs vermeiden, damit es zu keinem Heißlaufen kommt. Wenn der Batchjob abgeschlossen ist, können Sie nur aus der neuen Sammlung lesen.

Eine Verfeinerung dieser Strategie besteht darin, kleine Batches von Nutzern gleichzeitig zu migrieren. Fügen Sie dem Nutzerdokument ein Feld hinzu, das den Migrationsstatus des entsprechenden Nutzers verfolgt. Wählen Sie einen Batch von zu migrierenden Nutzern basierend auf einem Hash-Wert der Nutzer-ID aus. Verwenden Sie einen Batchjob, um Dokumente für diesen Batch von Nutzern zu migrieren, und verwenden Sie parallele Lesevorgänge für Nutzer, die gerade migriert werden.

Beachten Sie, dass ein Rollback nur dann problemlos ausgeführt werden kann, wenn Sie bei der Migration doppelte Schreibvorgänge für die alten wie für die neuen Entitäten vornehmen. Dadurch steigen allerdings die anfallenden Firestore-Kosten.

Datenschutz

  • Vermeiden Sie die Speicherung vertraulicher Informationen in einer Cloud-Projekt-ID. Eine Cloud-Projekt-ID könnte über die Lebensdauer Ihres Projekts hinaus beibehalten werden.
  • Aus Gründen der Datenanforderungen empfehlen wir, vertrauliche Informationen nicht in Dokumentnamen und Dokumentfeldnamen zu speichern.