Optimierung der DynamoDB-Dateneinspeisung mit begrenzter Kapazität
Vor kurzem hatten wir eine Dateneinspeiseaufgabe. Das bedeutet, dass eine große Anzahl von Quelldateien aus S3 geladen, die Informationen gelesen und in Amazon DynamoDB geschrieben werden müssen. Hier sind einige Dinge, die wir bedacht haben, als wir mit einer Lösung auftauchten.
- Die Gesamtzahl der S3-Quelldateien betrug etwa 1500 und jede Datei enthielt 4 Millionen Zeilen, die wir 6 Milliarden Mal in DynamoDB schreiben mussten.
- Amazon DynamoDB-Schriften sind teuer. Die Preise für (100 Write/Sekunde sind $3000/Monat), daher mussten wir die Schreibrate kontrollieren, da alles, was über die Kapazität hinausgeht, verloren geht.
- Unsere Anwendung würde in Pods laufen, die von Kubernetes verwaltet werden. Wir wollten sicherstellen, dass jede Kapsel voll ausgenutzt wird.
- Jede App wäre ein multi-threaded Java-Programm, das die Dinge komplizierter machen würde, aber auch helfen würde, die Aufgabe so schnell wie möglich abzuschließen.
Hier ist das Verhältnis zwischen der Schreibkapazität von DynamoDB, der Anzahl der Pod und der Anzahl der Threads:
Writing Capacity(writes/second) = (number of threads) * (number of pods) * (number of rows each thread can handle per second)
Es ist sehr einfach und wir brauchen einen Weg, um sicherzustellen, dass diese Gleichung erfüllt ist. Guave-Bibliothek (https://github.com/google/guava) kommt mir zunächst in den Sinn. Es verfügt über einen gut entwickelten Ratenbegrenzer und kann vor jedem Schreiben in DynamoDB aufgerufen werden. Zum Beispiel:
int numberOfWritesPerSecond = 100;
RateLimiter Limiter = RateLimiter.create(numberOfWritesPerSecond);
limiter.acquire()
updateDynamoDB();
Jetzt wird die Gleichung einfacher:
Writing Capacity(writes/second) = (number of writes on Rate limiter)* (number of pods)
Es sieht gut aus, aber als wir es getestet haben, hatte es Probleme:
- Wenn wir 3000 Schreibkapazität auf Amazon, 5 Pods in Kubernetes einstellen und jedem Pod 5 Threads geben würden, hätte ein einzelner Thread: 3000 / 5 / 5 = 120 (schreibt pro Sekunde) und eine Quelldatei konnte nur von einem Thread verarbeitet werden, wir hatten 4 Millionen Datensätze, so dass es 4 Millionen / 240 = 3333333 Sekunden oder 9 Stunden dauern würde, eine Datei zu beenden.
- Es ist eigentlich sogar länger als 9 Stunden, denn für eine Zeile in der Datei gibt es 2 Operationen: Lesen von S3 und Schreiben von Daten in Dynamo. Der langsamere Betrieb begrenzt die aktuelle Zeit.
- Bei einer so langen Verarbeitungszeit könnte die S3-Verbindung ausfallen, der Thread-Pool könnte ausfallen und/oder Kubernetes könnte neu starten. Zu viele Dinge können passieren, die den Job zum Scheitern bringen.
- Die Erhöhung der Schreibfähigkeit funktionierte nicht einmal. Die Kapazität eines einzelnen Threads wurde durch die Lese- und Schreibgeschwindigkeit von Dynamo begrenzt. Jede Datei hat noch eine hohe Ausfallwahrscheinlichkeit.
Hier ist, wie man Probleme löst:
- Nach sorgfältiger Beobachtung der Datei haben wir festgestellt, dass wir eine kleine Aggregation durchführen können. Alle 4 Zeilen können zu einer Zeile zusammengefasst und gemeinsam in DynamoDB geschrieben werden. 4 Millionen Schreiber wurden plötzlich zu 1 Million.
- Wir haben auf die asynchrone Dynamo-Funktion umgestellt, um die Dynamo-Schreibbegrenzung zu überwinden. Nun war die einzige Einschränkung das Lesen von Dateien. Wir haben die Anzahl der Threads in einem Pod abgestimmt, um das beste CPU-Dienstprogramm zu erhalten.
- Wir lesen eine Datei mit Multithreads.
Nachdem wir die ersten 3 Änderungen vorgenommen hatten, konnte eine Datei innerhalb von 2 Stunden fertig gestellt werden.
Fazit
Aus dieser Aufgabe haben wir ein tiefes Verständnis dafür gewonnen, wie man den Anwendungsdurchsatz eines Containerclusters steuert. Kubernetes vereinfacht die Container-Orchestrierung, bringt aber gleichzeitig mehr Komplexität in die Anwendung. Cloud-basierte Datenbanken (Dynamo) reduzieren die Wartungskosten, erfordern aber zusätzliche Arbeit, um sicherzustellen, dass die App unter ihren Einschränkungen korrekt funktioniert.