web-dev-qa-db-fra.com

Apache Spark: Séparation d'une paire de RDD en plusieurs RDD par clé pour enregistrer les valeurs

J'utilise Spark 1.0.1 pour traiter une grande quantité de données. Chaque ligne contient un numéro d'identification, certaines avec des identifiants en double. Je souhaite enregistrer toutes les lignes portant le même numéro d'identification au même emplacement, mais je ne parviens pas à le faire efficacement. Je crée un RDD [(String, String)] de paires (numéro d'identification, ligne de données):

val mapRdd = rdd.map{ x=> (x.split("\\t+")(1), x)} 

Une méthode efficace, mais non performante, consiste à collecter les numéros d'ID, à filtrer le RDD pour chaque ID et à enregistrer le RDD de valeurs avec le même ID sous forme de fichier texte.

val ids = rdd.keys.distinct.collect
ids.foreach({ id =>
    val dataRows = mapRdd.filter(_._1 == id).values
    dataRows.saveAsTextFile(id)
})

J'ai également essayé groupByKey ou reductionByKey afin que chaque tuple du RDD contienne un numéro d'identification unique en tant que clé et une chaîne de lignes de données combinées séparées par de nouvelles lignes pour ce numéro d'identification. Je souhaite parcourir le RDD une seule fois en utilisant foreach pour enregistrer les données, mais il ne peut pas donner les valeurs en tant que RDD

groupedRdd.foreach({ tup =>
  val data = sc.parallelize(List(tup._2)) //nested RDD does not work
  data.saveAsTextFile(tup._1)
})

Essentiellement, je souhaite diviser un RDD en plusieurs RDD par un numéro d’identification et enregistrer les valeurs de ce numéro d’identification dans leur propre emplacement.

31
smli

Je pense que ce problème est similaire à Écrire sur plusieurs sorties par clé Spark - un travail Spark

S'il vous plaît se référer la réponse là-bas.

import org.Apache.hadoop.io.NullWritable

import org.Apache.spark._
import org.Apache.spark.SparkContext._

import org.Apache.hadoop.mapred.lib.MultipleTextOutputFormat

class RDDMultipleTextOutputFormat extends MultipleTextOutputFormat[Any, Any] {
  override def generateActualKey(key: Any, value: Any): Any = 
    NullWritable.get()

  override def generateFileNameForKeyValue(key: Any, value: Any, name: String): String = 
    key.asInstanceOf[String]
}

object Split {
  def main(args: Array[String]) {
    val conf = new SparkConf().setAppName("Split" + args(1))
    val sc = new SparkContext(conf)
    sc.textFile("input/path")
    .map(a => (k, v)) // Your own implementation
    .partitionBy(new HashPartitioner(num))
    .saveAsHadoopFile("output/path", classOf[String], classOf[String],
      classOf[RDDMultipleTextOutputFormat])
    spark.stop()
  }
}

Je viens de voir une réponse similaire ci-dessus, mais en réalité nous n’avons pas besoin de partitions personnalisées. MultipleTextOutputFormat créera un fichier pour chaque clé. Il est normal que plusieurs enregistrements avec les mêmes clés entrent dans la même partition. 

new HashPartitioner (num), où num correspond au numéro de la partition souhaitée. Si vous avez un grand nombre de clés différentes, vous pouvez définir le nombre sur grand. Dans ce cas, chaque partition n'ouvrira pas trop de gestionnaires de fichiers hdfs.

13
zhang zhan

Cela permettra d'économiser les données par ID utilisateur

val mapRdd = rdd.map{ x=> (x.split("\\t+")(1),
x)}.groupByKey(numPartitions).saveAsObjectFile("file")

Si vous devez récupérer les données à nouveau en fonction de l'ID utilisateur, vous pouvez faire quelque chose comme: 

val userIdLookupTable = sc.objectFile("file").cache() //could use persist() if data is to big for memory  
val data = userIdLookupTable.lookup(id) //note this returns a sequence, in this case you can just get the first one  

Notez qu'il n'y a pas de raison particulière pour enregistrer dans le fichier dans ce cas. Je le fais juste depuis que l'OP l'a demandé, cela étant dit, l'enregistrement dans un fichier vous permet de charger le RDD à tout moment après le regroupement initial.

Une dernière chose, lookup est plus rapide qu’une approche de filtre permettant d’accéder aux identifiants, mais si vous êtes prêt à retirer une demande d’attraction de spark, vous pouvez effectuer un achat cette réponse pour une approche plus rapide

0
aaronman

vous pouvez appeler directement saveAsTextFile sur un RDD groupé. Ici, les données seront basées sur les partitions. Par exemple, si vous avez 4 identifiants distincts et que vous avez spécifié le nombre de partitions du groupedRDD à 4, vous ne pouvez avoir qu’un seul ID fileper). Vous pouvez même voir les données sous forme de données itérables de eachId dans le système de fichiers.

0
napster