Mon cluster: 1 maître, 11 esclaves, chaque nœud a 6 Go de mémoire.
Mes paramètres:
spark.executor.memory=4g, Dspark.akka.frameSize=512
Voici le problème:
Premièrement , je lis des données (2,19 Go) de HDFS à RDD:
val imageBundleRDD = sc.newAPIHadoopFile(...)
Deuxièmement , faites quelque chose sur ce RDD:
val res = imageBundleRDD.map(data => {
val desPoints = threeDReconstruction(data._2, bg)
(data._1, desPoints)
})
Last , sortie sur HDFS:
res.saveAsNewAPIHadoopFile(...)
Quand je lance mon programme, il montre:
.....
14/01/15 21:42:27 INFO cluster.ClusterTaskSetManager: Starting task 1.0:24 as TID 33 on executor 9: Salve7.Hadoop (NODE_LOCAL)
14/01/15 21:42:27 INFO cluster.ClusterTaskSetManager: Serialized task 1.0:24 as 30618515 bytes in 210 ms
14/01/15 21:42:27 INFO cluster.ClusterTaskSetManager: Starting task 1.0:36 as TID 34 on executor 2: Salve11.Hadoop (NODE_LOCAL)
14/01/15 21:42:28 INFO cluster.ClusterTaskSetManager: Serialized task 1.0:36 as 30618515 bytes in 449 ms
14/01/15 21:42:28 INFO cluster.ClusterTaskSetManager: Starting task 1.0:32 as TID 35 on executor 7: Salve4.Hadoop (NODE_LOCAL)
Uncaught error from thread [spark-akka.actor.default-dispatcher-3] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem[spark]
Java.lang.OutOfMemoryError: Java heap space
Il y a trop de tâches?
PS: Tout va bien lorsque les données d'entrée sont d'environ 225 Mo.
Comment puis-je résoudre ce problème?
J'ai quelques suggestions:
spark.executor.memory=6g
. Assurez-vous que vous utilisez le plus de mémoire possible en vérifiant l'interface utilisateur (elle indiquera la quantité de mémoire utilisée)spark.storage.memoryFraction
. Si vous n'utilisez pas cache()
ou persist
dans votre code, il peut également s'agir de 0. Sa valeur par défaut est 0,6, ce qui signifie que vous n'obtenez que 0.4 * 4g de mémoire pour votre segment de mémoire. IME réduisant le mem frac fait souvent disparaître les MOO. UPDATE: À partir de spark 1.6, apparemment, nous n'aurons plus besoin de jouer avec ces valeurs, spark les déterminera automatiquement.String
et fortement imbriquées (telles que les classes Map
et les cas imbriqués). Si possible, essayez d'utiliser uniquement des types primitifs et d'indexer toutes les non-primitives, en particulier si vous attendez beaucoup de doublons. Choisissez WrappedArray
plutôt que des structures imbriquées. Ou même déployez votre propre sérialisation - VOUS aurez le plus d’informations sur la manière de sauvegarder efficacement vos données en octets, USE IT!Dataset
pour mettre en cache votre structure car elle utilisera une sérialisation plus efficace. Cela devrait être considéré comme un bidouillage par rapport au point précédent. Construire votre connaissance de domaine dans votre algo/sérialisation peut réduire la mémoire/espace de cache de 100x ou 1000x, alors que tout ce que Dataset
donnera sera 2x - 5x en mémoire et 10x compressé (parquet) sur disque.http://spark.Apache.org/docs/1.2.1/configuration.html
EDIT: (pour que je puisse moi-même google plus facilement) Ce qui suit est aussi révélateur de ce problème:
Java.lang.OutOfMemoryError : GC overhead limit exceeded
Pour ajouter un cas d'utilisation à cela qui n'est souvent pas abordé, je vais proposer une solution lors de la soumission d'une application Spark
via spark-submit
en mode local.
D'après le gitbook Mastering Apache Spark de Jacek Laskowski :
Vous pouvez exécuter Spark en mode local. Dans ce mode de déploiement à une seule machine virtuelle Java non distribuée, Spark génère tous les composants d'exécution (pilote, exécuteur, serveur et maître) dans la même machine virtuelle. C'est le seul mode dans lequel un pilote est utilisé pour l'exécution.
Ainsi, si vous rencontrez des erreurs OOM
avec heap
, il suffit d’ajuster le driver-memory
plutôt que le executor-memory
.
Voici un exemple:
spark-1.6.1/bin/spark-submit
--class "MyClass"
--driver-memory 12g
--master local[*]
target/scala-2.10/simple-project_2.10-1.0.jar
Vous devriez augmenter la mémoire du pilote. Dans votre dossier $ SPARK_HOME/conf, vous devriez trouver le fichier spark-defaults.conf
, éditer et définir le spark.driver.memory 4000m
en fonction de la mémoire de votre maître, je pense .
Regardez les scripts de démarrage une taille de segment de mémoire Java est définie ici.
# Set SPARK_MEM if it isn't already set since we also use it for this process
SPARK_MEM=${SPARK_MEM:-512m}
export SPARK_MEM
# Set Java_OPTS to be able to load native libraries and to set heap size
Java_OPTS="$OUR_Java_OPTS"
Java_OPTS="$Java_OPTS -Djava.library.path=$SPARK_LIBRARY_PATH"
Java_OPTS="$Java_OPTS -Xms$SPARK_MEM -Xmx$SPARK_MEM"
Vous pouvez trouver la documentation pour déployer des scripts ici .
Vous devez configurer les paramètres de mémoire offHeap comme indiqué ci-dessous:
`val spark = SparkSession
.builder()
.master("local[*]")
.config("spark.executor.memory", "70g")
.config("spark.driver.memory", "50g")
.config("spark.memory.offHeap.enabled",true)
.config("spark.memory.offHeap.size","16g")
.appName("sampleCodeForReference")
.getOrCreate()`
Donnez à la mémoire du pilote et de la mémoire de l'exécuteur la disponibilité de vos machines RAM. Vous pouvez augmenter la taille offHeap si vous êtes toujours confronté au problème OutofMemory .
De manière générale, la mémoire JVM spark Executor peut être divisée en deux parties. Mémoire Spark et mémoire utilisateur. Ceci est contrôlé par la propriété spark.memory.fraction
- la valeur est comprise entre 0 et 1. Lorsque vous travaillez avec des images ou effectuez un traitement gourmand en mémoire dans les applications spark, envisagez de réduire le spark.memory.fraction
. Cela rendra plus de mémoire disponible pour votre travail d'application. Spark peut se répandre, donc il fonctionnera toujours avec moins de partage de mémoire.
La deuxième partie du problème est la division du travail. Si possible, partitionnez vos données en morceaux plus petits. De plus petites données nécessitent éventuellement moins de mémoire. Mais si cela n’est pas possible, vous devez faire des sacrifices pour la mémoire. Généralement, un seul exécutant exécutera plusieurs cœurs. La mémoire totale des exécutants doit être suffisante pour gérer les besoins en mémoire de toutes les tâches simultanées. Si l'augmentation de la mémoire de l'exécutant n'est pas une option, vous pouvez réduire le nombre de cœurs par exécuteur afin que chaque tâche utilise davantage de mémoire. Testez avec 1 exécuteur principal qui dispose de la plus grande mémoire possible, puis continuez à augmenter le nombre de cœurs jusqu’à ce que vous trouviez le meilleur nombre de cœurs.
L'emplacement dans lequel définir la taille de la mémoire mémoire (au moins dans spark-1.0.0) se trouve dans conf/spark-env . Les variables appropriées sont SPARK_EXECUTOR_MEMORY
& SPARK_DRIVER_MEMORY
. D'autres documents sont disponibles dans guide de déploiement
De plus, n'oubliez pas de copier le fichier de configuration sur tous les nœuds esclaves.
Avez-vous vidé votre journal maître gc? J'ai donc rencontré un problème similaire et j'ai découvert que SPARK_DRIVER_MEMORY ne définissait que le tas Xmx. La taille de segment initiale reste 1G et la taille de segment n'augmente jamais jusqu'au segment Xmx.
Passer "--conf" spark.driver.extraJavaOptions = -Xms20g "résout mon problème.
ps aux | grep Java et vous verrez le journal suivant: =
24501 30.7 1.7 41782944 2318184 pts/0 Sl + 18:49 0:33/usr/Java/dernières/bin/Java -cp/opt/spark/conf /:/opt/spark/jars/* - Xmx30g -Xms20g
J'ai beaucoup souffert de ce problème, nous utilisons l'allocation de ressources dynamique et je pensais qu'il utiliserait les ressources de mon cluster pour s'adapter au mieux à l'application.
Mais la vérité est que l’allocation dynamique des ressources ne définit pas la mémoire du pilote et la maintient à sa valeur par défaut, 1g.
Je l'ai résolu en définissant spark.driver.memory sur un nombre qui convient à la mémoire de mon pilote (pour 32 Go de RAM, je l'ai réglé à 18 Go).
vous pouvez le définir en utilisant la commande spark submit comme suit:
spark-submit --conf spark.driver.memory=18gb ....cont
Note très importante, cette propriété ne sera pas prise en compte si vous la définissez à partir du code, selon la documentation de spark:
Les propriétés Spark peuvent principalement être divisées en deux types: l'une liée au déploiement, comme «spark.driver.memory», «spark.executor.instances», ce type de propriétés peut ne pas être affecté lors de la définition par programme de SparkConf à l'exécution Le comportement dépend du gestionnaire de grappe et du mode de déploiement que vous choisissez. Il est donc suggéré de définir les options via un fichier de configuration ou une ligne de commande spark-submit; une autre est principalement liée au contrôle d'exécution Spark, comme «spark.task.maxFailures», ce type de propriétés peut être défini de l'une ou l'autre manière.
J'ai peu de suggestions pour l'erreur mentionnée ci-dessus.
● Vérifiez que la mémoire de l'exécuteur attribuée en tant qu'exécuteur peut avoir à traiter avec des partitions nécessitant plus de mémoire que celle affectée.
● Essayez de voir s'il y a plus de shuffles en direct, ce qui coûte cher, car ils impliquent des E/S de disque, une sérialisation des données et des E/S réseau
● Utiliser les jointures de diffusion
● Évitez d’utiliser GroupByKeys et essayez de le remplacer par RéductionByKey.
● Évitez d'utiliser d'énormes objets Java chaque fois que vous mélangez