Spark 1.0.1を使用して大量のデータを処理します。各行にはID番号が含まれ、一部には重複したIDが含まれます。同じID番号を持つすべての行を同じ場所ですが、効率的に実行するのに問題があります(ID番号、データ行)のペアのRDD [(String、String)]を作成します。
val mapRdd = rdd.map{ x=> (x.split("\\t+")(1), x)}
動作しますが、パフォーマンスは低下しますが、ID番号を収集し、各IDのRDDをフィルター処理し、テキストファイルと同じIDの値のRDDを保存します。
val ids = rdd.keys.distinct.collect
ids.foreach({ id =>
val dataRows = mapRdd.filter(_._1 == id).values
dataRows.saveAsTextFile(id)
})
また、groupByKeyまたはreduceByKeyを試して、RDDの各タプルにキーとして一意のID番号と、そのID番号の改行で区切られた結合データ行の文字列が含まれるようにしました。 foreachを使用してデータを保存するためにRDDを1回だけ繰り返したいが、RDDとして値を与えることができない
groupedRdd.foreach({ tup =>
val data = sc.parallelize(List(tup._2)) //nested RDD does not work
data.saveAsTextFile(tup._1)
})
基本的に、ID番号によってRDDを複数のRDDに分割し、そのID番号の値を独自の場所に保存する必要があります。
この問題は キーによる複数の出力への書き込みSpark-one Spark job
回答を参照してください。
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()
}
}
上記の同様の回答を見ましたが、実際にはカスタマイズされたパーティションは必要ありません。 MultipleTextOutputFormatは、各キーのファイルを作成します。同じキーを持つ複数のレコードが同じパーティションに分類されることは問題ありません。
new HashPartitioner(num)。numは必要なパーティション番号です。多数の異なるキーがある場合は、numberをbigに設定できます。この場合、各パーティションは多くのhdfsファイルハンドラーを開きません。
これにより、ユーザーIDごとにデータが保存されます
val mapRdd = rdd.map{ x=> (x.split("\\t+")(1),
x)}.groupByKey(numPartitions).saveAsObjectFile("file")
ユーザーIDに基づいてデータを再度取得する必要がある場合は、次のようなことができます
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
この場合、ファイルに保存する特別な理由はないことに注意してください。私はOPがそれを要求したので、私はそれをしただけです。ファイルに保存すると言われているので、最初のグループ化が完了した後いつでもRDDをロードできます。
最後に、lookup
はIDにアクセスするフィルターアプローチよりも高速ですが、sparkからプルリクエストを送信する場合は、チェックアウトできます this answer より速いアプローチのために