假设我有一个RDD(50M记录/ dayredu),我想以几种不同的方式进行总结。
RDD记录是4元组:(keep, foo, bar, baz)
。
keep
- 布尔foo
,bar
,baz
- 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个管道。
答案 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不是列表,它们是分布式的,因此您提供了两个函数参数,一个用于操作每个分区,另一个用于组合处理分区的结果。