J'effectue un réglage de la mémoire sur mon travail Spark sur YARN et je remarque que différents paramètres donneraient des résultats différents et affecteraient le résultat de l'exécution du travail Spark. Cependant , Je suis confus et je ne comprends pas complètement pourquoi cela se produit et j'apprécierais que quelqu'un puisse me fournir des conseils et des explications.
Je vais fournir quelques informations de base et poster mes questions et décrire les cas que j'ai rencontrés après eux ci-dessous.
Mes paramètres d'environnement étaient les suivants:
Mon code filtre récursivement un RDD pour le rendre plus petit (en supprimant des exemples dans le cadre d'un algorithme), puis fait mapToPair et collecte pour rassembler les résultats et les enregistrer dans une liste.
Pourquoi une erreur différente est-elle générée et le travail s'exécute plus longtemps (pour le deuxième cas) entre le premier et le deuxième cas, seule la mémoire de l'exécuteur étant augmentée? Les deux erreurs sont-elles liées d'une manière ou d'une autre?
Le troisième et le quatrième cas réussissent et je comprends que c'est parce que je donne plus de mémoire qui résout les problèmes de mémoire. Cependant, dans le troisième cas,
spark.driver.memory + spark.yarn.driver.memoryOverhead = la mémoire que YARN créera une machine virtuelle Java
= 11g + (driverMemory * 0,07, avec un minimum de 384m) = 11g + 1,154g = 12,154g
Ainsi, à partir de la formule, je peux voir que mon travail nécessite MEMORY_TOTAL d'environ 12.154g pour s'exécuter correctement, ce qui explique pourquoi j'ai besoin de plus de 10g pour le paramètre de mémoire du pilote.
Mais pour le quatrième cas,
spark.driver.memory + spark.yarn.driver.memoryOverhead = la mémoire que YARN créera une machine virtuelle Java
= 2 + (driverMemory * 0,07, avec un minimum de 384m) = 2g + 0,524g = 2,524g
Il semble que juste en augmentant la surcharge de la mémoire d'une petite quantité de 1024 (1g), cela mène à l'exécution réussie du travail avec une mémoire de pilote de seulement 2g et le MEMORY_TOTAL n'est que de 2,524g! Alors que sans la configuration de surcharge, la mémoire du pilote inférieure à 11g échoue, mais cela n'a aucun sens dans la formule, c'est pourquoi je suis confus.
Pourquoi augmenter la surcharge de mémoire (pour le pilote et l'exécuteur) permet à mon travail de se terminer avec succès avec un MEMORY_TOTAL inférieur (12.154g contre 2.524g)? Y a-t-il d'autres choses internes au travail ici qui me manquent?
/bin/spark-submit --class <class name> --master yarn-cluster --driver-memory 7g --executor-memory 1g --num-executors 3 --executor-cores 1 --jars <jar file>
Si j'exécute mon programme avec une mémoire de pilote inférieure à 11 g, j'obtiendrai l'erreur ci-dessous qui est le SparkContext arrêté ou une erreur similaire qui est une méthode appelée sur un SparkContext arrêté. D'après ce que j'ai rassemblé, cela est lié au fait que la mémoire ne suffit pas.
/bin/spark-submit --class <class name> --master yarn-cluster --driver-memory 7g --executor-memory 3g --num-executors 3 --executor-cores 1 --jars <jar file>
Si j'exécute le programme avec la même mémoire de pilote mais une mémoire d'exécuteur plus élevée, le travail s'exécute plus longtemps (environ 3-4 minutes) que le premier cas, puis il rencontrera une erreur différente de la précédente, qui est un conteneur demandant/utilisant plus de mémoire que autorisé et est tué à cause de cela. Bien que je trouve cela bizarre car la mémoire de l'exécuteur est augmentée et cette erreur se produit à la place de l'erreur dans le premier cas.
/bin/spark-submit --class <class name> --master yarn-cluster --driver-memory 11g --executor-memory 1g --num-executors 3 --executor-cores 1 --jars <jar file>
Tout paramètre dont la mémoire du pilote est supérieure à 10 g permettra au travail de s'exécuter correctement.
/bin/spark-submit --class <class name> --master yarn-cluster --driver-memory 2g --executor-memory 1g --conf spark.yarn.executor.memoryOverhead=1024 --conf spark.yarn.driver.memoryOverhead=1024 --num-executors 3 --executor-cores 1 --jars <jar file>
Le travail s'exécutera avec succès avec ce paramètre (mémoire du pilote 2g et mémoire de l'exécuteur 1g mais en augmentant la surcharge de la mémoire du pilote (1g) et la surcharge de la mémoire de l'exécuteur (1g).
Toute aide sera appréciée et aiderait vraiment à ma compréhension de Spark. Merci d'avance.
Tous vos cas utilisent
--executor-cores 1
C'est la meilleure pratique d'aller au-dessus de 1. Et de ne pas aller au-dessus de 5. D'après notre expérience et de Spark recommandation des développeurs.
Par exemple. http://blog.cloudera.com/blog/2015/03/how-to-tune-your-Apache-spark-jobs-part-2/ :
A rough guess is that at most five tasks per executor
can achieve full write throughput, so it’s good to keep
the number of cores per executor below that number
Je ne trouve pas maintenant de référence où il était recommandé d'aller au-dessus de 1 cœur par exécuteur. Mais l'idée est que l'exécution de plusieurs tâches dans le même exécuteur vous permet de partager certaines régions de mémoire communes afin d'économiser de la mémoire.
Commencez avec --executor-cores 2, double --executor-memory (car --executor-cores indique également combien de tâches un exécuteur exécutera simultanément), et voyez ce qu'il fait pour vous. Votre environnement est compact en termes de mémoire disponible, donc passer à 3 ou 4 vous donnera une meilleure utilisation de la mémoire.
Nous utilisons Spark 1.5 et avons cessé d'utiliser --executor-cores 1 il y a un certain temps car il posait des problèmes de GC; il ressemble également à un bogue Spark, parce que donner plus de mémoire n'aide pas autant que de passer à plus de tâches par conteneur. Je suppose que les tâches dans le même exécuteur peuvent augmenter sa consommation de mémoire à différents moments, donc vous ne gaspillez pas/n'avez pas à surprovisionner mémoire juste pour le faire fonctionner.
Un autre avantage est que les variables partagées de Spark (accumulateurs et variables de diffusion) auront une seule copie par exécuteur, pas par tâche - donc passer à plusieurs tâches par exécuteur est une économie de mémoire directe. Même si vous n'utilisez pas Spark variables partagées explicitement, Spark les crée très probablement de toute façon en interne. Par exemple, si vous joignez deux tables via Spark SQL, le CBO de Spark peut décider de diffuser une table plus petite (ou une trame de données plus petite) sur l'ensemble pour accélérer l'exécution des jointures.
http://spark.Apache.org/docs/latest/programming-guide.html#shared-variables