Optimisation de l'injection de données DynamoDB avec une capacité limitée
Récemment, nous avons eu une tâche d'injection de données. Cela signifie que le chargement d'une grande quantité de fichiers source de S3, la lecture des informations, et de l'écrire à Amazon DynamoDB. Voici certaines choses que nous avons considérées comme nous avons trouvé une solution.
- Le nombre total de fichiers source S3 était d'environ 1500 et chaque fichier contenait 4 millions de lignes, nous avons dû écrire à DynamoDB 6 milliards fois.
- Amazon DynamoDB écrit sont chers. Prix pour (100 écrit/seconde est de $3000/mois), nous avons donc dû contrôler le taux d'écriture, parce que tout ce que la capacité serait perdu.
- Notre application serait en cours d'exécution dans les pods gérés par Kubernetes. Nous voulions nous assurer que chaque POD serait pleinement utilisé.
- Chaque application serait un programme multi-threaded Java, cela rendrait les choses plus compliquées, mais aussi aider à la finition de la tâche dès que possible.
Voici la relation entre la capacité d'écriture DynamoDB, le nombre de Pod, et le nombre de threads:
Writing Capacity(writes/second) = (number of threads) * (number of pods) * (number of rows each thread can handle per second)
Il est très simple et nous avons besoin d'un moyen de s'assurer que cette équation est satisfaite. Bibliothèque de goyave (https://github.com/google/guava) vient à l'esprit au début. Il a un limiteur de taux bien conçu et peut être appelé avant chaque écriture à DynamoDB. Par exemple:
int numberOfWritesPerSecond = 100;
RateLimiter limiter = RateLimiter. Create (numberOfWritesPerSecond);
limiter. Acquire ()
updateDynamoDB ();
Maintenant, l'équation devient plus simple:
Writing Capacity(writes/second) = (number of writes on Rate limiter)* (number of pods)
Il semble bon, mais quand nous l'avons testé, il avait des problèmes:
- Si nous avons mis 3000 capacité d'écriture sur Amazon, 5 gousses dans Kubernetes et a donné à chaque Pod 5 threads, un seul thread aurait: 3000/5/5 = 120 (écrit par seconde) et un fichier source ne pouvait être traitée par un thread, nous avons eu 4 millions de dossiers , il faudrait donc 4 millions/240 = 33333 secondes ou 9 heures pour terminer un fichier.
- Il est en fait encore plus de 9 heures parce que pour une rangée dans le fichier, il ya 2 opérations: la lecture de S3 et l'écriture de données à dynamo. L'opération la plus lente limite le temps réel.
- Avec un temps de traitement si long, la connexion S3 pourrait temps, le pool de threads pourrait temps, et/ou Kubernetes pourrait redémarrer. Trop de choses pourraient arriver à faire échouer le travail.
- Augmenter la capacité d'écriture n'a même pas fonctionné. La capacité de filetage unique a été limitée par la lecture de fichiers et la vitesse d'écriture Dynamo. Chaque fichier a encore une grande possibilité d'échouer.
Voici comment résoudre les problèmes:
- Après une observation attentive du dossier, nous avons constaté que nous pouvons faire une petite agrégation sur elle. Toutes les 4 rangées peuvent être combinées en une rangée et écrites à DynamoDB ensemble. 4 millions écrit est soudainement devenu 1 million.
- Nous avons changé à l'opération de dynamo asynchrone pour surmonter la limitation d'écriture de dynamo. Maintenant, la seule limite était la lecture de fichiers. Nous avons accordé le nombre de threads dans un pod pour obtenir le meilleur utilitaire CPU.
- Nous lisons un fichier avec des multi-threads.
Après que nous ayons fait les 3 premiers changements, un fichier pourrait être fini dans les 2 heures.
Conclusion
À partir de cette tâche, nous avons acquis une profonde compréhension sur la façon de contrôler le débit d'application d'un cluster de conteneurs. Kubernetes facilite l'orchestration du conteneur, mais en même temps il apporte plus de complexité à l'application. La base de données basée sur les nuages (Dynamo) réduit le coût de maintenance, mais nécessite un travail supplémentaire pour s'assurer que l'application fonctionne correctement sous ses contraintes.