是否将同一RDD上的多个reduceByKey编译成单次扫描?

时间:2015-03-20 18:52:03

标签: apache-spark

假设我有一个RDD(50M记录/ dayredu),我想以几种不同的方式进行总结。 RDD记录是4元组:(keep, foo, bar, baz)

  • keep - 布尔
  • foobarbaz - 0/1 int

我想计算每个foo& c中有多少被保留和删除,即我必须对foo执行以下操作(bar也是如此和baz):

rdd.filter(lambda keep, foo, bar, baz: foo == 1)
   .map(lambda keep, foo, bar, baz: keep, 1)
   .reduceByKey(operator.add)

会返回(collect之后)[(True,40000000),(False,10000000)]之类的列表。

问题是:是否有 简单 方式可以避免扫描rdd 3次(每次foo一次,{{1 },bar)?

我的意思是不是一种重写上述代码来处理所有3个字段的方法,但是告诉spark在一次传递中处理所有3个管道。

2 个答案:

答案 0 :(得分:3)

通过提交具有不同线程的作业,可以并行执行三个管道,但这将通过RDD三次,并且需要群集上多达3倍的资源。

通过重写作业以一次处理所有计数,可以一次性完成工作 - 关于aggregate的答案是一个选项。成对分割数据(keep, foo) (keep, bar), (keep, baz)将是另一个。

不可能在一次通过中完成工作而不进行任何代码更改,因为Spark无法知道这些作业与同一数据集相关。在caching步骤之前,rdd.cache初始 rdd 可以在.filter().map().reduce()之后提高后续作业的速度;这仍将通过RDD 3次,但如果所有数据都适合群集的内存,则第2次和第3次可能会快得多:

rdd.cache
// first reduceByKey action will trigger the cache and rdd data will be kept in memory
val foo = rdd.filter(fooFilter).map(fooMap).reduceByKey(???)
// subsequent operations will execute faster as the rdd is now available in mem
val bar = rdd.filter(barFilter).map(barMap).reduceByKey(???)
val baz = rdd.filter(bazFilter).map(bazMap).reduceByKey(???)

如果我这样做,我会创建一对相关数据并一次性计算:

// We split the initial tuple into pairs keyed by the data type ("foo", "bar", "baz") and the keep information. dataPairs will contain data like: (("bar",true),1), (("foo",false),1)

val dataPairs = rdd.flatmap{case (keep, foo, bar, baz) => 
    def condPair(name:String, x:Int):Option[((String,Boolean), Int)] = if (x==1) Some(((name,keep),x)) else None
    Seq(condPair("foo",foo), condPair("bar",bar), condPair("baz",baz)).flatten
}
val totals = dataPairs.reduceByKey(_ + _)

这是 easy ,只会将数据传递一次,但需要重写代码。我会说在回答这个问题时得分<66,66%。

答案 1 :(得分:1)

如果我正确地阅读了您的问题,您需要RDD.aggregate

val zeroValue = (0L, 0L, 0L, 0L, 0L, 0L) // tfoo, tbar, tbaz, ffoo, fbar, fbaz
rdd.aggregate(zeroValue)(
  (prior, current) => if (current._1) {
    (prior._1 + current._2, prior._2 + current._3, prior._3 + current._4,
      prior._4, prior._5, prior._6)
  } else {
    (prior._1, prior._2, prior._3,
      prior._4 + current._2, prior._5 + current._3, prior._6 + current._4)
  },
  (left, right) =>
    (left._1 + right._1,
      left._2 + right._2,
      left._3 + right._3,
      left._4 + right._4,
      left._5 + right._5,
      left._6 + right._6)
)

聚合在概念上类似于列表上的概念性reduce函数,但RDD不是列表,它们是分布式的,因此您提供了两个函数参数,一个用于操作每个分区,另一个用于组合处理分区的结果。