Эффективная группировка по ключу с помощью mapPartitions или разделителя в Spark

Итак, у меня есть данные, подобные следующему,

[ (1, data1), (1, data2), (2, data3), (1, data4), (2, data5) ]

который я хочу преобразовать в следующий для дальнейшей обработки.

[ (1, [data1, data2, data4]), (2, [data3, data5]) ]

Я использовал groupByKey и reduceByKey, но из-за очень большого объема данных это не удается. Данные не высокие, но широкие. Другими словами, ключи от 1 до 10000, но список значений варьируется от 100k до 900k.

Я борюсь с этой проблемой и планирую применить mapPartitions или (Hash)partitioner.

Итак, если что-то из этого может сработать, я хотел бы знать

  1. Используя mapPartions, не могли бы вы дать фрагмент кода?
  2. Используя (Hash)partitioner, не могли бы вы привести пример того, как управлять разделами с помощью какого-либо элемента, такого как ключ.. например. есть ли способ создать каждый раздел на основе ключа (т.е. 1,2,.. выше) без необходимости перемешивания.

Exception in thread "main" org.apache.spark.SparkException: Job aborted due to stage failure: ShuffleMapStage 9 (flatMap at TSUMLR.scala:209) has failed the maximum allowable number of times: 4. Most recent failure reason: org.apache.spark.shuffle.MetadataFetchFailedException: Missing an output location for shuffle 1
        at org.apache.spark.MapOutputTracker$$anonfun$org$apache$spark$MapOutputTracker$$convertMapStatuses$2.apply(MapOutputTracker.scala:542)
        at org.apache.spark.MapOutputTracker$$anonfun$org$apache$spark$MapOutputTracker$$convertMapStatuses$2.apply(MapOutputTracker.scala:538)
        at scala.collection.TraversableLike$WithFilter$$anonfun$foreach$1.apply(TraversableLike.scala:772)
        at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
        at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:108)
        at scala.collection.TraversableLike$WithFilter.foreach(TraversableLike.scala:771)
        at org.apache.spark.MapOutputTracker$.org$apache$spark$MapOutputTracker$$convertMapStatuses(MapOutputTracker.scala:538)
        at org.apache.spark.MapOutputTracker.getMapSizesByExecutorId(MapOutputTracker.scala:155)
        at org.apache.spark.shuffle.BlockStoreShuffleReader.read(BlockStoreShuffleReader.scala:47)
        at org.apache.spark.rdd.ShuffledRDD.compute(ShuffledRDD.scala:98)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:306)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:270)
        at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
        at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:306)
        at org.apache.spark.rdd.RDD.iterator(RDD.scala:270)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:73)
        at org.apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:41)
        at org.apache.spark.scheduler.Task.run(Task.scala:89)
        at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:213)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:745)

person joshsuihn    schedule 26.01.2016    source источник


Ответы (1)


Ни один из предложенных способов не сработал. Разделитель по определению должен перемешивать данные и будет страдать от тех же ограничений, что и groupByKey. mapPartitions не может перемещать данные в другой раздел, поэтому он совершенно бесполезен. Поскольку ваше описание проблемы довольно расплывчато, трудно дать конкретный совет, но в целом я бы попробовал следующие шаги:

  • попробуйте переосмыслить проблему. Вам действительно нужны все значения сразу? Как вы планируете их использовать? Можно ли получить те же результаты, не собирая в один раздел?
  • можно ли уменьшить трафик? Сколько уникальных значений вы ожидаете? Можно ли сжать данные перед перемешиванием (например, подсчитать значения или использовать RLE)?
  • рассмотрите возможность использования более крупных исполнителей. Spark должен хранить в памяти только значения одного ключа и может сбрасывать обработанные ключи на диск.
  • разделите ваши данные по ключу:

    val keys =  rdd.keys.distinct.collect
    val rdds = keys.map(k => rdd.filter(_._1 == k))
    

    и обрабатывать каждый RDD отдельно.

person zero323    schedule 05.02.2016
comment
Ваш совет действительно отличный! ) Это действительно полезно. - person joshsuihn; 08.02.2016