spark 1. Guide de programmation

Présentation

Tout d'abord, chaque application spark possède un pilote pour exécuter la fonction principale et les opérations parallèles sur chaque nœud.
Spark fournit un ensemble de données RDD (Resilient Distributed Data Set), qui peut être utilisé en parallèle via différents nœuds et peut être construit via des fichiers hdfs. RDD peut être mis en cache en mémoire et sera plus efficace lorsqu'il doit être réutilisé.

Deuxièmement, des variables partagées (varibales partagées) sont fournies pour une utilisation dans des opérations parallèles sur différents nœuds. L'une concerne les variables de diffusion et l'autre les accumulateurs. Vous pouvez utiliser ces deux outils lorsqu'une variable doit être à nouveau partagée dans différentes tâches de nœud ou lorsque la tâche de nœud et le pilote voient les exigences de partage de variable.

Connecter l'étincelle

from pyspark import SparkContext, SparkConf
  • SparkContext: une interface utilisée pour connecter des nœuds de cluster
  • SparkConf:
  1. Configuration de l'application Spark, définissez divers paramètres Spark sous forme de paires clé-valeur. Lorsque le programme démarre, il lira automatiquement divers paramètres de configuration du système. Si SparkConf est configuré, SparkConf aura la priorité.
  2. Le paramètre SparkConf(false)peut être évité dans le paramètre du système externe, qui est entièrement soumis à SparkConf.
  3. Tous les paramètres de configuration prennent en charge le chaînage, tels que:

SparkConf().setMaster(“local”).setAppName(“My app”)

Initialiser l'étincelle

from pyspark import SparkContext, SparkConf

conf = SparkConf().setAppName('programm_guide')
sc = SparkContext(conf=conf)
print ('the sc is: {}'.format(sc))

Utiliser un shell

Ajoutez le répertoire spark bin aux variables d'environnement et exécutez directement

pyspark

RDD

Collections parallélisées

data = [1, 2, 3, 4, 5]
distData = sc.parallelize(data)

Utilisez l'interface sc.parallelize pour créer un RDD à partir d'un itérateur ou d'une collection. Les données de l'itérateur ou de la collection seront copiées et créées sous la forme d'un ensemble de données distribuées flexible qui peut être calculé en parallèle ou RDD.

Habituellement, les données RDD seront partitionnées, vous pouvez spécifier des paramètres pour spécifier le nombre de partitions dans lesquelles les données sont divisées. Par exemple:

sc.parallelize(data, 10))

Jeu de données externe

distFile = sc.textFile("data.txt")

Spark prend en charge la lecture des données pour créer des RDD des manières suivantes:

  1. HDFS
  2. Cassandra
  3. HBase
  4. Amazon S3
  5. local

Spark prend en charge la lecture directe des fichiers texte, SequenceFiles et autres formats d'entrée hadoop.

Notez les points suivants lorsque vous utilisez spark pour lire des fichiers:

  1. Si le chemin de lecture est un chemin local, vous devez vous assurer que le même chemin sur le nœud doit exister et être accessible.
  2. Toutes les opérations de lecture de fichiers spark telles que sc.textFile tous les dossiers de prise en charge, les fichiers compressés et les noms de fichiers génériques. Par exemple:
rdd = sc.textFile("/my/directory")
rdd = sc.textFile("/my/directory/*.txt")
rdd = sc.textFile("/my/directory/*.gz")

Autre:

  1. SparkContext.wholeTextFiles
    Prend en charge la lecture de nombreux petits fichiers à partir d'un dossier et renvoie RDD selon les paires (nom de fichier, contenu). En revanche, la méthode de sc.textFile lit le dossier et renvoie un RDD, et chaque ligne de chaque fichier est un enregistrement RDD. .
  2. RDD.saveAsPickleFileEt SparkContext.pickleFile
    pour la séquence RDD dans un objet python format.
  3. Enregistrer et charger SequenceFiles
rdd = sc.parallelize(range(1, 4)).map(lambda x: (x, "a" * x))
rdd.saveAsSequenceFile("path/to/file")
sorted(sc.sequenceFile("path/to/file").collect())
  1. Enregistrez et chargez des données à partir d'autres formats d'entrée et de sortie hadoop: ...

Fonctionnement RDD

RDD prend en charge deux types d'opérations:

1. transformations

Toutes les transformations sont effectuées paresseusement, comme la carte. Pour le dire simplement, toutes les transformations ne sont pas exécutées immédiatement. Le programme sait quelles transformations il a effectuées sur les données et l'ordre d'exécution correspondant. Ce n'est que lorsqu'il rencontre une action qu'il calcule et renvoie le résultat à Pilotes. La conception implémentée par lazy peut rendre le programme spark plus efficace.

Le RDD après chaque transformation peut être mis en cache et réutilisé par rdd.persist () ou rdd.cache ().

2. Les actions sont l'
opposé de la transformation et sont exécutées immédiatement.

Fondation

lines = sc.textFile("data.txt")
lineLengths = lines.map(lambda s: len(s))
lineLengths.persist()

totalLength = lineLengths.reduce(lambda a, b: a + b)

Lire les données, couper et persister, réduire le fonctionnement.

Passer la fonction pour déclencher

Trois façons:

  • expression lambda
rdd.map(lambda s: len(s))
  • fonction définie par défaut
def myFunc(s):
    words = s.split(" ")
    return len(words)

sc = SparkContext(...)
sc.textFile("file.txt").map(myFunc)

Étant donné que le nœud accédant aux propriétés d'un objet externe conduira à référencer l'objet entier, il est préférable d'opérer en copiant une variable locale dans la fonction.

class MyClass(object):
    def __init__(self):
        self.field = "Hello"
    def doStuff(self, rdd):
        field = self.field
        return rdd.map(lambda s: field + s)
  • Certaines fonctions de module avancées encapsulées par spark lui-même

Comprendre les fermetures

L'une des difficultés pour comprendre l'étincelle est de comprendre avec précision la portée et le cycle de vie des variables et des méthodes lors de l'exécution de code sur plusieurs nœuds. Les opérations RDD qui modifient des variables hors plage peuvent prêter à confusion. Jetons foreach()un œil au cas passé ci-dessous .

Cas

counter = 0
rdd = sc.parallelize(data)

# Wrong: Don't do this!!
def increment_counter(x):
    global counter
    counter += x
rdd.foreach(increment_counter)

print("Counter value: ", counter)

Ce code est utilisé pour compter le nombre d'enregistrements dans rdd. Notez que cette écriture est fausse!

Mode local vs mode nœud

Ce qui précède n'est pas correct. L'exécuteur spark décomposera l'opération rdd en tâches, et chaque tâche est exécutée par l'exécuteur. Avant d'exécuter la tâche, spark calculera la fermeture de la tâche, c'est-à-dire les variables et les méthodes qui doivent être visibles lorsque le programme d'exécution est calculé, et l'enverra à chaque exécuteur.

Dans le code ci-dessus, la variable compteur a été copiée et envoyée au programme d'exécution de chaque nœud, ce qui équivaut à la variable locale du programme d'exécution de différents nœuds. Chaque nœud change la variable locale. Le compteur du pilote final est toujours 0.

Lors de l'exécution du programme en mode local, parfois la fonction foreach sera exécutée dans la même machine virtuelle Java que le pilote et référencera le compteur global, puis le programme sera valide. Mais cette façon d'écrire n'est pas encore la manière recommandée d'écrire.

Afin de résoudre les problèmes dans les scénarios ci-dessus, spark fournit un accumulateur (Accumulator), une utilisation détaillée sera présentée plus tard.

Imprimer les éléments RDD

Utilisez pour rdd.collect()obtenir la valeur en rdd, puis imprimez. Mais ce n'est pas un bon moyen. Parce que l'opération de collecte résumera toutes les données du rdd sur une machine, lorsque le rdd a beaucoup de données, il n'y aura plus de mémoire.

rdd.foreach(println)Pas de la même manière, la sortie standard sera sortie vers différents nœuds.

La bonne approche devrait être :rdd.take(100).foreach(println)

Gérer les paires de données clé-valeur

La plupart des opérations RDD prennent en charge tout type de données, mais certaines opérations ne prennent en charge que les données de valeur-clé. Les opérations les plus courantes sont les opérations de mélange réparties, telles que les groupes et les agrégats.

lines = sc.textFile("data.txt")
pairs = lines.map(lambda s: (s, 1))
counts = pairs.reduceByKey(lambda a, b: a + b)

transformations

Pour plus de détails, voir: Document d'interface RDD

Actions

Pour plus de détails, voir: Document d'interface RDD

API Spark RDD prend en charge les opérations asynchrones, comme pour foreachla foreachAsync.

fonctionnement aléatoire

shuffle est un mécanisme de redistribution des données afin que les données puissent être regroupées différemment entre les partitions. L'implication de la copie de données sur des programmes exécutifs et des machines est une opération compliquée et coûteuse.

Contexte

Prenons l'exemple de l'opération ReduceByKey.

Toutes les valeurs d'une même clé peuvent ne pas être dans la même partition ou même sur la même machine, mais la valeur de la même clé doit être calculée ensemble.

Lors du calcul de Spark, une seule tâche opère sur une seule partition. Pour organiser toutes les valeurs de la même clé, spark doit lire toutes les données de toutes les partitions et agréger les valeurs de la même clé pour obtenir le résultat final. C'est aléatoire.

Les opérations qui provoquent le shuffle incluent les opérations de répartition telles que la partition et la fusion; les opérations ByKey telles que groupByKey et ReduceByKey et les opérations de jointure telles que cogroup et join.

Impact sur les performances

La lecture aléatoire implique les E / S disque, la sérialisation des données, les E / S réseau et des opérations relativement coûteuses.

Les opérations de réduction de la clé et d'agrégation de la clé consomment de la mémoire de pile supplémentaire. Les opérations de vidage de données impliquées dans ces opérations sont toutes organisées en structures de données de mémoire. Lorsque les données ne conviennent pas aux opérations de mémoire, une surcharge d'E / S disque supplémentaire et un garbage collection se produisent.

Shuffle générera un grand nombre de fichiers intermédiaires et spark conservera temporairement ces fichiers jusqu'à ce qu'il n'utilise plus le RDD correspondant avant le garbage collection. Si le programme conserve des références au RDD correspondant ou si le garbage collection n'est pas démarré fréquemment, la tâche spark plus longue prendra beaucoup d'espace disque.

Endurance RDD

RDD doit utiliser la technologie de persistance de RDD lorsqu'elle est réutilisée dans le programme pour accélérer le programme.

rdd.persist()
rdd.cache()

Il existe différents niveaux de stockage pour la persistance
image

MEMORY_ONLY est l'option par défaut de rdd.cache ().

Sélection du niveau de stockage

Le choix du niveau de stockage est un compromis entre l'utilisation de la mémoire et l'efficacité du processeur. Il est recommandé de choisir selon les principes suivants:

  1. La valeur par défaut MEMORY_ONLY est un choix très efficace pour le processeur. Si le programme fonctionne correctement, essayez de ne pas le modifier.
  2. Si le programme fonctionne mal, essayez MEMORY_ONLY_SER (uniquement pour JAVA / SCALA)
  3. Si vous souhaitez réparer rapidement le défaut, utilisez le niveau de stockage basé sur la réplication. Il y aura une meilleure tolérance aux pannes pendant l'exécution du programme et les données perdues seront automatiquement récupérées de la sauvegarde pour continuer le calcul.

supprimer des données

Spark surveille l'utilisation de la mémoire de chaque nœud et effectue la récupération de place selon le principe LRU (le moins récemment utilisé). Vous pouvez également supprimer manuellement un RDD via RDD.unpersist ().

Variable partagée

Variable de diffusion

broadcastVar = sc.broadcast([1, 2, 3])
broadcastVar.value

La variable partagée créée par sc.broadcast peut être transmise à chaque nœud pour appeler. Une fois livré, il ne doit pas être modifié par le conducteur. Le programme d'exécution de chaque nœud obtient la même valeur.

Accumulateurs

Deux façons

  1. sc.accumulator
accum = sc.accumulator(0)
sc.parallelize([1, 2, 3, 4]).foreach(lambda x: accum.add(x))
accum.value

sc.accumulator (0) donne à accum une valeur initialisée. accum peut être exploité par tous les nœuds à l'aide de la méthode add, mais la valeur ne peut pas être lue. Seul le pilote peut lire sa valeur via accm.value.

  1. Hériter de la classe AccumulatorParam , l'utilisation spécifique de cette chose doit encore être étudiée.
class VectorAccumulatorParam(AccumulatorParam):
    def zero(self, initialValue):
        return Vector.zeros(initialValue.size)

    def addInPlace(self, v1, v2):
        v1 += v2
        return v1

# Then, create an Accumulator of this type:
vecAccum = sc.accumulator(Vector(...), VectorAccumulatorParam())

Déployer

Pour plus de détails, voir: Documents officiels

Je suppose que tu aimes

Origine www.cnblogs.com/Fosen/p/12689035.html
conseillé
Classement