Was ist der schnellste Weg, um viele Dokumente in Firestore zu schreiben?

11

Ich muss eine große Anzahl von Dokumenten an Firestore schreiben.

Was ist der schnellste Weg, dies in Node.js zu tun?

Frank van Puffelen
quelle

Antworten:

26

TL; DR: Der schnellste Weg zur Erstellung von Massendaten im Firestore besteht darin, parallele Einzelschreibvorgänge durchzuführen.

Das Schreiben von 1.000 Dokumenten an Firestore erfordert:

  1. ~105.4s bei Verwendung sequentieller Einzelschreibvorgänge
  2. ~ 2.8s bei Verwendung von (2) Stapelschreibvorgängen
  3. ~ 1.5s bei Verwendung paralleler Einzelschreibvorgänge

Es gibt drei gängige Methoden, um eine große Anzahl von Schreibvorgängen im Firestore auszuführen.

  1. Führen Sie jede einzelne Schreiboperation nacheinander aus.
  2. Verwenden von Stapelschreibvorgängen.
  3. Parallele Ausführung einzelner Schreibvorgänge.

Wir werden sie nacheinander anhand eines Arrays randomisierter Dokumentdaten untersuchen.


Einzelne sequentielle Schreibvorgänge

Dies ist die einfachste mögliche Lösung:

async function testSequentialIndividualWrites(datas) {
  while (datas.length) {
    await collection.add(datas.shift());
  }
}

Wir schreiben jedes Dokument der Reihe nach, bis wir jedes Dokument geschrieben haben. Und wir warten, bis jeder Schreibvorgang abgeschlossen ist, bevor wir mit dem nächsten beginnen.

Das Schreiben von 1.000 Dokumenten dauert bei diesem Ansatz ungefähr 105 Sekunden, sodass der Durchsatz ungefähr 10 Dokumentschreibvorgänge pro Sekunde beträgt .


Verwenden von Stapelschreibvorgängen

Dies ist die komplexeste Lösung.

async function testBatchedWrites(datas) {
  let batch = admin.firestore().batch();
  let count = 0;
  while (datas.length) {
    batch.set(collection.doc(Math.random().toString(36).substring(2, 15)), datas.shift());
    if (++count >= 500 || !datas.length) {
      await batch.commit();
      batch = admin.firestore().batch();
      count = 0;
    }
  }
}

Sie können sehen, dass wir ein BatchedWriteObjekt erstellen, indem Sie es aufrufen batch(), es bis zu seiner maximalen Kapazität von 500 Dokumenten füllen und es dann in Firestore schreiben. Wir geben jedem Dokument einen generierten Namen, der relativ wahrscheinlich eindeutig ist (gut genug für diesen Test).

Das Schreiben von 1.000 Dokumenten dauert bei diesem Ansatz ungefähr 2,8 Sekunden, sodass der Durchsatz ungefähr 357 Dokumentschreibvorgänge pro Sekunde beträgt .

Das ist ziemlich viel schneller als bei den sequentiellen einzelnen Schreibvorgängen. Tatsächlich verwenden viele Entwickler diesen Ansatz, weil sie davon ausgehen, dass er am schnellsten ist. Wie die obigen Ergebnisse bereits gezeigt haben, ist dies jedoch nicht der Fall. Und der Code ist aufgrund der Größenbeschränkung für Stapel bei weitem der komplexeste.


Parallele individuelle Schreibvorgänge

In der Firestore-Dokumentation wird Folgendes über die Leistung beim Hinzufügen vieler Daten angegeben :

Verwenden Sie für die Eingabe von Massendaten eine Server-Client-Bibliothek mit parallelisierten einzelnen Schreibvorgängen. Batch-Schreibvorgänge sind besser als serialisierte Schreibvorgänge, jedoch nicht besser als parallele Schreibvorgänge.

Wir können das mit diesem Code testen:

async function testParallelIndividualWrites(datas) {
  await Promise.all(datas.map((data) => collection.add(data)));
}

Dieser Code startet die addVorgänge so schnell wie möglich und Promise.all()wartet dann, bis alle abgeschlossen sind. Mit diesem Ansatz können die Operationen parallel ausgeführt werden.

Das Schreiben von 1.000 Dokumenten dauert bei diesem Ansatz ungefähr 1,5 Sekunden, sodass der Durchsatz ungefähr 667 Dokumentschreibvorgänge pro Sekunde beträgt .

Der Unterschied ist nicht annähernd so groß wie zwischen den ersten beiden Ansätzen, aber immer noch mehr als 1,8-mal schneller als bei Batch-Schreibvorgängen.


Ein paar Anmerkungen:

  • Den vollständigen Code dieses Tests finden Sie auf Github .
  • Während der Test mit Node.js durchgeführt wurde, erhalten Sie wahrscheinlich ähnliche Ergebnisse auf allen Plattformen, die das Admin SDK unterstützt.
  • Führen Sie jedoch keine Masseneinfügungen mit Client-SDKs durch, da die Ergebnisse sehr unterschiedlich und viel weniger vorhersehbar sein können.
  • Wie üblich hängt die tatsächliche Leistung von Ihrem Computer, der Bandbreite und Latenz Ihrer Internetverbindung und vielen anderen Faktoren ab. Basierend auf diesen können Sie auch Unterschiede in den Unterschieden sehen, obwohl ich davon ausgehe, dass die Reihenfolge gleich bleibt.
  • Wenn Sie in Ihren eigenen Tests Ausreißer haben oder völlig andere Ergebnisse finden, hinterlassen Sie unten einen Kommentar.
  • Batches-Schreibvorgänge sind atomar. Wenn Sie also Abhängigkeiten zwischen den Dokumenten haben und alle Dokumente geschrieben werden müssen oder keines von ihnen geschrieben werden muss, sollten Sie einen Stapelschreibvorgang verwenden.
Frank van Puffelen
quelle
1
Das ist super interessant, danke für die Arbeit! OOC, haben Sie getestet, wie die Stapelschreibvorgänge parallel ausgeführt werden? In diesem Fall müssten Sie natürlich noch sicherer sein, um zu vermeiden, dass sich Dokumente in beiden Stapeln befinden.
Robsiemb
1
Ich wollte gerade parallele Stapelschreibvorgänge testen, hatte aber kein Kontingent mehr (es ist ein kostenloses Projekt und ich war zu faul, um ein Upgrade durchzuführen). Heute ist ein anderer Tag, also könnte ich es versuchen und meine Antwort aktualisieren, wenn es wichtig ist.
Frank van Puffelen
2
@robsiemb Ich habe gerade auch mit parallelen Batch-Schreibvorgängen getestet. Die Leistung ist den einzelnen parallelen Schreibvorgängen sehr ähnlich, daher würde ich sagen, dass sie in meinen Tests an erster Stelle stehen. Ich gehe davon aus, dass sich gestapelte Schreibvorgänge aufgrund der Art, wie sie im Back-End verarbeitet werden, schneller verschlechtern können. In Kombination mit dem viel komplexeren Code würde ich dennoch empfehlen, sie nur wegen ihrer Atomizität und nicht wegen des wahrgenommenen, aber nicht vorhandenen Leistungsvorteils zu verwenden.
Frank van Puffelen
@FrankvanPuffelen Parallelisierte Schreibvorgänge sind auch dann schneller, wenn ich Dokumente "setze" anstatt Dokumente "hinzuzufügen"? Ich meine, db.collection ('Städte'). Doc ('LA'). Set (Daten) anstelle von db.collection ('Städte'). Add (Daten)
alek6dj
Beim Aufrufen add()wird lediglich eine eindeutige ID (rein clientseitig) generiert, gefolgt von einer set()Operation. Die Ergebnisse sollten also gleich sein. Wenn Sie dies nicht beobachten, stellen Sie eine neue Frage mit dem Minimalfall, der das reproduziert, was Sie versucht haben.
Frank van Puffelen