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 Hauptbeitrag zur Schreiblatenz ist der Index-Fanout. Die Best Practices zur Reduzierung des Indexfanouts sind:

  • Legen Sie Indexausnahmen auf Sammlungsebene fest. Eine einfache Standardeinstellung ist die Deaktivierung von Absteigend und Array-Indexierung. Wenn Sie nicht verwendete indexierte Werte entfernen, 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 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 (Time-to-Live) verwenden, muss das Feld „TTL“ einen Zeitstempel enthalten. Die Indexierung für TTL-Felder ist standardmäßig aktiviert und kann die Leistung bei höheren Traffic-Raten beeinträchtigen. Fügen Sie als Best Practice Einzelfeldausnahmen für Ihre TTL-Felder hinzu.

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 Anwendung 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 verstehen.

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 Ihre App einzelne Dokumente aktualisiert. Lasttests sind die beste Möglichkeit, die Leistung Ihrer Arbeitslast zu bestimmen. Die genaue maximale Rate, mit der eine Anwendung ein einzelnes Dokument aktualisieren kann, hängt stark von der Arbeitslast ab. Zu den Faktoren gehören die Schreibrate, Konflikte zwischen Anfragen und die Anzahl der betroffenen Indexe.

Ein Schreibvorgang für ein Dokument aktualisiert das Dokument und alle zugehörigen Indexe und Firestore wendet den Schreibvorgang synchron auf ein Quorum von Replikaten an. Bei ausreichend hohen Schreibraten treten in der Datenbank Konflikte, höhere Latenz oder andere Fehler auf.

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:

  • Erstellt neue Dokumente mit einer sehr hohen Rate und weist eigene kontinuierlich ansteigende 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.

Gelöschte Daten nicht überspringen

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

Ein Beispiel für eine Arbeitslast, die viele gelöschte Daten überspringen muss, ist eine Arbeitslast, die versucht, 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 kürzlich gelöschten Dokumenten gescannt. Dies verlangsamt Abfragen.

Verwenden Sie die Methode start_at, um den besten Startpunkt zu finden und so die Leistung zu verbessern. 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 obigen Beispiel wird ein monoton wachsendes Feld verwendet, was ein Antimuster für hohe Schreibraten darstellt.

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 es, vertrauliche Informationen in einer Cloud-Projekt-ID zu speichern. Eine Cloud-Projekt-ID kann über die Lebensdauer Ihres Projekts hinaus beibehalten werden.
  • Als Best Practice für die Datencompliance empfehlen wir, keine vertraulichen Informationen in Dokumentnamen und Dokumentfeldnamen zu speichern.