Un élément d'optimisation des performances Spark suffit

1. Optimisation des performances Spark: développement et optimisation

1. Évitez de créer des RDD en double.
Pour le même élément de données, un seul RDD doit être créé et plusieurs RDD ne peuvent pas être créés pour représenter le même élément de données. Sinon, le travail Spark calculera à plusieurs reprises plusieurs RDD représentant les mêmes données, ce qui augmentera la surcharge de performances du travail.
2. Réutiliser le même RDD autant que possible.
Si les données de plusieurs RDD se chevauchent ou contiennent, nous devons réutiliser un RDD autant que possible, afin que le nombre de RDD puisse être réduit autant que possible, réduisant ainsi le nombre d'exécutions d'opérateurs autant que possible.
3. Persistez le RDD utilisé plusieurs fois ou chickPoint
chaque fois que vous effectuez une opération d'opérateur sur un RDD, il recalculera à partir de la source, calculera le RDD, puis exécutera votre opérateur sur ce RDD en fonctionnement. Conserver le RDD utilisé plusieurs fois. À ce stade, Spark enregistrera les données du RDD dans la mémoire ou le disque en fonction de votre stratégie de persistance. Dans le futur, chaque fois que ce RDD est utilisé, les données RDD persistantes seront extraites directement de la mémoire ou du disque pour éviter des calculs répétés.
Stratégie de persistance, la meilleure performance par défaut est MEMORY_ONLY, mais le principe est que la mémoire doit être suffisante Large, peut stocker toutes les données de l'ensemble du RDD. Évitez la surcharge des performances des opérations de sérialisation et de désérialisation.
Si beaucoup de données RDD provoquent un débordement de mémoire jvm (MOO), il est recommandé d'essayer d'utiliser MEMORY-ONLY_SER, de changer le niveau pour sérialiser les données RDD, puis de les enregistrer en mémoire. À ce stade, la partition est juste un tableau d'octets, ce qui réduit considérablement l'objet Ce niveau réduit l'utilisation de la mémoire. Ce niveau entraîne une surcharge de performances de sérialisation et de désérialisation plus élevée que MEMORY_ONLY. Si le nombre de RDD est trop important, cela entraînera toujours un dépassement de capacité de la mémoire. Il est alors recommandé d'utiliser MEMORY_AND_DISK_SER. Cette stratégie donnera la priorité à la tentative de données En mémoire cache, il sera écrit sur le disque uniquement s'il ne peut pas être stocké à l'intérieur.
Utilisez la syntaxe RDD.persist (StorageLevel.MEMORY_ONLY_SER) comme ceci.
Par conséquent, le niveau de persistance de la sérialisation peut être encore optimisé, c'est-à-dire utiliser la bibliothèque de sérialisation Kryo, afin que vous puissiez obtenir une vitesse de sérialisation plus rapide et occuper moins d'espace mémoire, mais rappelez-vous, Si l'élément RDD est un type personnalisé, enregistrez le type personnalisé dans Kryo à l'avance. Si vous souhaitez garantir des performances élevées lorsque les données persistantes du RDD peuvent être perdues, vous pouvez vérifier le RDD.
4. Essayez d'éviter d'utiliser des opérateurs de lecture aléatoire.
Lors de l'exécution de tâches Spark, la partie la plus gourmande en performances est le processus de lecture aléatoire. Dans notre processus de développement, évitez d'utiliser réduireByKey, join, distinct, repartition, etc. pour mélanger autant que possible. Pour les opérateurs, essayez d'utiliser des opérateurs non-shuffle de la classe map. Dans ce cas, les travaux Spark sans opérations de lecture aléatoire ou avec moins d'opérations de lecture aléatoire peuvent réduire considérablement la surcharge de performances.
5. Utilisez des opérateurs hautes performances
1) Utilisation raisonnable de reductionByKey et groupByKey.
Lorsque cela est possible, il est recommandé d'utiliser reductionByKey au lieu de groupByKey, car l'opérateur reductionByKey utilisera une fonction personnalisée pour pré-agréger la même clé localement sur chaque nœud. groupByKey ne sera pas pré-agrégé. La totalité des données sera distribuée et transmise entre les limites du cluster. Les performances sont relativement médiocres.
2) Utilisez mapPartitons pour remplacer les
opérateurs mapPartitions ordinaires . Un appel de fonction traitera une partition. Toutes les données, au lieu de traiter un appel de fonction à la fois, les performances seront relativement plus élevées.
3) Utiliser foreachPartitions au lieu de foreach
est également un appel de fonction pour traiter toutes les données d'une partition, plutôt qu'un appel de fonction pour traiter un élément de données. Il est toujours utile d'améliorer les performances
4) Effectuer l'opération de fusion après avoir utilisé le filtre.
Habituellement, après avoir filtré plus de données en exécutant un opérateur de filtre sur un RDD, il est recommandé d'utiliser l'opérateur de fusion pour réduire manuellement le nombre de partitions RDD et compresser les données dans RDD en moins de partitions. Tant que vous utilisez moins de tâches, vous pouvez
traiter toutes les partitions
6. Les grandes variables de diffusion
utilisent des variables externes dans la fonction opérateur. Par défaut, Spark affectera plusieurs copies de la variable à la tâche via le réseau. Chaque tâche a une copie. Si la variable elle-même est relativement grande, la surcharge de performances d'un grand nombre de copies de variables dans la transmission réseau et la mémoire excessive occupée dans l'Excutor de chaque nœud provoquent des GC fréquents, ce qui affecte grandement les performances. Par conséquent, il est recommandé d'utiliser la fonction de diffusion de Spark pour diffuser le montant modifié afin de garantir qu'une seule copie de la variable réside dans la mémoire de chaque exécuteur. La copie est partagée lorsque la tâche est exécutée. Réduisant ainsi la surcharge de performance de la transmission réseau, réduisant la surcharge de la mémoire de l'Executor et réduisant la fréquence de GC.7
Utilisez Kryo pour optimiser les performances de sérialisation
Si vous souhaitez utiliser le mécanisme de sérialisation Kryo, vous devez d'abord définir un paramètre avec SparkConf et utiliser le nouveau SparkConf (). set ("spark.serializer", "org.apache.spark.serializer.KryoSerializer"). Lors de l'utilisation de Kryo, il nécessite des classes de sérialisation, qui doivent être enregistrées en premier, et les meilleures performances ont été obtenues. Si vous ne vous enregistrez pas, Kryo doit toujours enregistrer le nom d'autorité complet du type, mais cela prend beaucoup de mémoire.
8. Optimiser la structure des données
1. Donnez la préférence aux tableaux et aux chaînes plutôt qu'aux collections. En d'autres termes, utilisez un tableau de préférence aux collections telles que ArrayList, LinkedList et HashMap.
2) Évitez d'utiliser une structure d'objets imbriqués à plusieurs niveaux.
3) Pour certains scénarios qui peuvent être évités, essayez d'utiliser int au lieu de String.
Dans l'implémentation du codage Spark, en particulier pour le code de la fonction opérateur, essayez d'utiliser des chaînes au lieu d'objets, utilisez des types primitifs (int, Long) au lieu de chaînes et utilisez des tableaux au lieu de types de collection, afin de réduire autant que possible l'utilisation de la mémoire, réduisant ainsi Fréquence GC pour améliorer les performances.

Deuxièmement, l'optimisation des performances Spark: réglage des ressources

1. num-executors
Ce paramètre est utilisé pour définir le nombre total de processus Executor à utiliser pour le travail Spark à exécuter. Lorsque le pilote envoie de vraies ressources au cluster Yarn, Yarn démarre autant de processus Executor que possible sur chaque nœud du groupe en fonction de votre messager 4 . Si vous ne le définissez pas, seul
un petit nombre de processus Executor sera démarré par défaut . À ce stade, la vitesse de fonctionnement de votre Spark est très lente.
Il est recommandé de définir généralement environ 50 à 100 tables de rapport selon le cas. S'il y en a trop peu, les ressources du cluster ne seront pas pleinement utilisées, et s'il y en a trop, la plupart des files d'attente peuvent ne pas être en mesure de fournir suffisamment de ressources.
2. executor-memory
Ce paramètre est utilisé pour définir la mémoire de chaque processus Executor. La taille de la
mémoire de l' exécuteur détermine souvent directement les performances du travail Spark. Il est recommandé que le paramètre de mémoire de chaque processus exécuteur soit compris entre 4 et 8 g. Le paramètre spécifique dépend toujours des files d'attente de ressources des différents services.
3. executor-cores
Ce paramètre est utilisé pour définir le nombre de cœurs de processeur pour chaque processus Executor. Ce paramètre détermine la capacité de chaque processus Executor à exécuter des threads de tâches en parallèle.
Il est plus approprié de définir le nombre de cœurs de processeur de l'Executor sur 2 ~ 4. Il doit également être déterminé en fonction des files d'attente de ressources des différents services. Vous pouvez voir quelle est la limite maximale de cœur de processeur de votre file d'attente de ressources, puis déterminer le nombre de cœurs de processeur que chaque processus Executor peut allouer à chaque processus Executor en fonction du nombre d'exécuteurs défini.
4. pilote- memory
Ce paramètre est utilisé pour définir la mémoire du processus
Driver.Généralement, la mémoire du Driver n'est pas définie ou elle devrait être suffisante pour définir environ 1G. L'opérateur de collecte qui doit être utilisé extrait les données RDD vers le pilote pour le traitement, puis la mémoire du pilote doit être suffisamment grande, sinon la mémoire débordera.
5.spark.default.parallelism
Ce paramètre est utilisé pour définir le nombre de tâches par défaut pour chaque étape. Ce paramètre est extrêmement important. S'il n'est pas défini, il peut affecter directement les performances de votre tâche Spark. Il est
recommandé que le nombre de tâches par défaut soit compris entre 500 et 1 000. S'il n'est pas défini, spark lui-même définit le nombre de tâches en fonction du nombre de blocs HDFS sous-jacents. Par défaut, un bloc HDFS correspond à une tâche. En conséquence, de nombreux processus Executor n'ont aucune exécution de tâche et les ressources sont gaspillées.
6.spark.storage.memoryFraction
Ce paramètre permet de définir le pourcentage de données persistantes RDD dans la mémoire de l'Executor. La valeur par défaut est 0,6. Par défaut, 60% de la mémoire de l'Executor peut être utilisée pour conserver des données RDD persistantes.
S'il y a plus d'opérations persistantes RDD dans le travail Spark, la valeur de ce paramètre peut être augmentée de manière appropriée pour garantir que les données persistantes peuvent être stockées dans la mémoire. Évitez de ne pas avoir suffisamment de mémoire pour mettre en cache toutes les données, ce qui entraîne l'écriture des données sur le disque uniquement, ce qui réduit les performances.
7.spark.shuffle.memoryFraction
Ce paramètre est utilisé pour définir la proportion de mémoire de l'exécuteur qui peut être utilisée pour les opérations d'agrégation après qu'une tâche a extrait la sortie de la tâche de l'étape précédente du processus de lecture aléatoire. La valeur par défaut est 0,2.
S'il y a peu d'opérations de persistance RDD dans le travail Spark et de nombreuses opérations de lecture aléatoire, il est recommandé de réduire le taux de mémoire de l'opération de persistance et d'augmenter le taux de mémoire de l'opération de lecture aléatoire pour éviter une mémoire insuffisante lorsqu'il y a trop de données dans le processus de lecture aléatoire et qu'un débordement doit être écrit Sur disque, ce qui réduit les performances.
En outre, si le travail s'exécute lentement en raison de la fréquence des GC, ce qui signifie que la mémoire pour la tâche pour exécuter le code utilisateur n'est pas suffisante, il est également recommandé de réduire la valeur de ce paramètre.

Troisièmement, l'optimisation des performances Spark: réglage de l'inclinaison des données

1. Filtrez un petit nombre de touches
qui causent l'inclinaison. Si vous constatez qu'il n'y a que quelques touches qui causent l'inclinaison et que l'impact sur le calcul lui-même n'est pas important, il est très approprié d'utiliser ce
principe de schéma : après avoir filtré les touches qui causent l'inclinaison des données, Ces touches ne participeront pas au calcul, et il est naturellement impossible de générer l'inclinaison des données.
Avantages: simple à mettre en œuvre, et l'effet est également très bon, permet d'éviter complètement l'inclinaison des données.
Inconvénients: il n'y a pas beaucoup de scénarios applicables. Dans la plupart des cas, il y a encore beaucoup de touches qui causent l'inclinaison, pas seulement quelques-unes.
2. Améliorer le parallélisme des opérations de shuffle.
Ce schéma est préféré car il s'agit du schéma le plus simple pour traiter le biais de données.
Principe: l'augmentation du nombre de tâches de lecture aléatoire permet d'affecter plusieurs clés initialement affectées à une tâche à plusieurs tâches, de sorte que chaque tâche puisse traiter moins de données que l'original.
Avantages: elle est relativement simple à mettre en œuvre, ce qui permet d'alléger et de réduire efficacement L'impact du biais des données.
Inconvénients: il soulage simplement le biais des données, n'élimine pas complètement le problème, et son effet est limité.
3. L'agrégation en deux étapes (agrégation locale + agrégation globale)
est plus appropriée lors de l'exécution d'opérateurs de mélange d'agrégation tels que reductionByKey sur RDD ou instructions group by dans Spark SQL pour l'agrégation de groupes.
Principe: En ajoutant un préfixe aléatoire à la même clé d'origine, elle devient plusieurs clés différentes, de sorte que les données initialement traitées par une tâche peuvent être distribuées à plusieurs tâches pour l'agrégation locale, résolvant ainsi le problème du traitement excessif des données par une seule tâche Beaucoup de questions. Supprimez ensuite le préfixe aléatoire et effectuez à nouveau une agrégation globale pour obtenir le résultat final.
Avantages: L'effet est très bon pour le biais de données causé par l'opération de lecture aléatoire de la classe d'agrégation. Habituellement, le biais de données peut être résolu, ou au moins le biais de données peut être considérablement soulagé, et les performances des travaux Spark peuvent être améliorées plusieurs fois.
Inconvénients: Applicable uniquement aux opérations de brassage agrégé, et le champ d'application est relativement restreint. S'il s'agit d'une opération aléatoire de la classe de jointure, d'autres solutions doivent être utilisées.
4. Convertissez une jointure réduite en jointure de mappage.
Lorsque vous utilisez des opérations de jointure sur des RDD ou des instructions de jointure dans Spark SQL, la quantité de données dans un RDD ou une table dans l'opération de jointure est relativement faible.
Principe: les jointures ordinaires disparaissent Le processus de lecture aléatoire, et une fois mélangé, équivaut à extraire les données de la même clé dans une tâche de lecture aléatoire, puis à rejoindre, à ce stade, il s'agit de réduire la jointure. Mais si un RDD est relativement petit, vous pouvez utiliser l'opérateur de diffusion de petites données complètes RDD + carte pour obtenir le même effet que la jointure, c'est-à-dire la jointure de carte. À ce stade, aucune opération de mélange ne se produira et aucun biais de données ne se produira. .
Avantages: L'effet est très bon pour le biais de données causé par l'opération de jointure, car la lecture aléatoire ne se produira pas du tout et le biais de données ne se produira pas du tout.
Inconvénients: il existe moins de scénarios applicables, car ce schéma ne convient que pour une grande table et une petite table. Après tout, nous devons diffuser la petite table, qui consommera plus de ressources mémoire en ce moment

Quatrièmement, l'optimisation des performances de Spark: réglage aléatoire

1. spark.shuffle.file.buffer
Ce paramètre est utilisé pour définir la taille de la mémoire tampon du bufferOutputStream de la tâche d'écriture aléatoire. La valeur par défaut est 32 Ko. Avant d'écrire les données dans le fichier disque, elles seront écrites dans la mémoire tampon. Une fois la mémoire tampon pleine, écrivez sur le disque.
Suggestion: Si les ressources mémoire disponibles pour le travail sont suffisantes, vous pouvez augmenter la taille de ce paramètre de manière appropriée ( Comme 64k), ce qui réduit le nombre de fois que le fichier disque est écrasé pendant le processus d'écriture aléatoire, ce qui peut également réduire le nombre d'E / S disque, améliorant ainsi les performances.
2. spark.reducer.maxSizeInFlight
Ce paramètre est utilisé pour définir la taille de la mémoire tampon de la tâche de lecture aléatoire, la valeur par défaut est de 48 m, et cette mémoire tampon détermine la quantité de données pouvant être extraite à chaque fois.
Recommandation: Si les ressources de mémoire disponibles pour le travail sont suffisantes, vous pouvez augmenter de manière appropriée la taille de ce paramètre (par exemple, 96 m) pour réduire le nombre de fois où extraire des données, ce qui peut également réduire le nombre de transmissions réseau, améliorant ainsi les performances.
3. spark.shuffle.io.maxRetries
par défaut: 3 Lorsque la
tâche de lecture aléatoire extrait ses propres données du nœud où se trouve la tâche d'écriture aléatoire, si l'extraction échoue en raison d'une anomalie du réseau, elle réessayera automatiquement. Ce paramètre représente le nombre maximal de tentatives pouvant être réessayées.
Suggestion: Pour les travaux qui contiennent des opérations de lecture aléatoire particulièrement chronophages, il est recommandé d'augmenter le nombre maximal de tentatives (par exemple, 60).
4. spark.shuffle.io.retryWait
valeur par défaut 5s
this Le paramètre représente l'intervalle d'attente pour chaque nouvelle tentative d'extraction de données et la valeur par défaut est de 5 s.
Recommandation: Il est recommandé d'augmenter l'intervalle de temps (par exemple 60 s) pour augmenter la stabilité de l'opération de lecture aléatoire.
5.spark.shuffle.memoryFraction
valeur par défaut: 0,2
Description du paramètre: Ce paramètre représente la proportion de mémoire allouée à la tâche de lecture aléatoire pour l'opération d'agrégation dans la mémoire de l'Executor, et la valeur par défaut est 20%.
Suggestion de réglage: ce paramètre a été expliqué dans le réglage des paramètres de ressource. Si la mémoire est suffisante et que les opérations persistantes sont rarement utilisées, il est recommandé d'augmenter ce ratio
6.spark.shuffle.manager
Valeur par défaut: sort
Ce paramètre permet de définir le type de ShuffleManager.
Suggestion: Puisque SortShuffleManager trie les données par défaut, si vous avez besoin de ce mécanisme de tri dans votre logique métier, vous pouvez utiliser le SortShuffleManager par défaut;
7.spark.shuffle.consolidateFiles
valeur par défaut: false
Si vous utilisez HashShuffleManager, ce paramètre est valide . S'il est défini sur true, le mécanisme de consolidation sera activé et les fichiers de sortie de l'écriture aléatoire seront fortement fusionnés. Dans le cas d'un nombre particulièrement élevé de tâches de lecture aléatoire, cette méthode peut réduire considérablement la surcharge d'E / S du disque et améliorer les performances.
Suggestion: Si vous n'avez pas besoin du mécanisme de tri de SortShuffleManager, en plus d'utiliser le mécanisme de contournement, vous pouvez également essayer de spécifier manuellement le paramètre spark.shffle.manager en tant que hachage, utiliser HashShuffleManager et activer le mécanisme consolidé.

Je suppose que tu aimes

Origine blog.csdn.net/weixin_43777152/article/details/109255182
conseillé
Classement