Spark:汇总与映射并减少

时间:2018-09-21 08:55:58

标签: apache-spark mapreduce

我正在学习Spark,并开始了解Spark如何分配数据并组合结果。 我得出的结论是,先使用 map 操作,再使用 reduce ,在仅使用操作 aggregate 方面具有优势。这是(至少我是这样认为的),因为 aggregate 使用顺序操作,这会损害并行性,而 map reduce 可以从完全并行性中受益。 因此,在进行选择时,使用map和reduce而不是总会更好吗?有没有首选聚集的情况?或者,当 aggregate 不能被 map reduce 组合代替时?

作为一个例子-我想找到最大长度的字符串:

val z = sc.parallelize(List("123","12","345","4567"))
// instead of this aggregate ....
z.aggregate(0)((x, y) => math.max(x, y.length), (x, y) => math.max(x, y))
// .... shouldn't I rather use this map - reduce combination ?
z.map(_.length).reduce((x, y) => math.max(x, y))

3 个答案:

答案 0 :(得分:0)

我相信我可以部分回答我自己的问题。我错误地认为,因为使用了顺序操作,所以 aggregate 的并行性可能会受到损害。数据仍然可以并行化,顺序操作将在每个块上执行。这似乎并不比 map 操作要差。因此,剩下的问题是:为什么要使用 aggregate 而不是 map-reduce 组合?

答案 1 :(得分:0)

聚合操作允许指定一个组合器功能(以减少通过随机播放发送的数据量),这与reducer不同,通过 map-reduce 组合,可以使用相同的功能进行组合和降低。我知道使用过旧的Map Reduce术语,但从概念上讲,所有这些都不共享基于shuffle的框架,并且如果您通过Google搜索Mapreduce组合器,将会发现很多有关该概念的解释。

答案 2 :(得分:0)

一个小例子可能会比冗长的解释更好。

假设您有一个Toto字段的类age。您有很多Toto,并且希望计算每个Toto的年龄总和。

final case class Toto(val age: Int)

val rdd = sc.parallelize(0 until n).map(Toto(_))

// map/reduce style
val sum1 = rdd
             // O(n) operations to go througth every Toto's age
             .map(_.age)
             // another O(n) to access data then O(n) operations to sum the n values
             .reduce(_ + _)
// You get the result with 2 pass over your data plus O(n) additions

// aggregate style
val sum2 = rdd.aggregate(0)((agg, e) => agg + e.age, _ + _)
// With one pass over the data, and O(n) additions you obtain the same result

如果您考虑访问权限和每个操作,会更加复杂。

因为总访问权限仍然存在,然后将年龄加总到代表 O(2.n)操作, O(n)访问权限加上 O(n )添加,以及聚合之间可忽略的合并操作。

在另一边具有地图/缩小样式的地图,首先,地图代表 O(n)访问,然后再次 O(n)访问数据以减少数据访问量 O(n)个附加操作的开销,总共需要 O(3.n)个操作。

别忘了Spark是懒惰的事实,您的所有转换都将通过最后的行动得到利用。

我认为使用聚合将节省一些操作,然后将缩短应用程序的运行时间。但是,根据您的操作,与聚合或CombineByKey(aggregateByKey的一般化)相比,表达连续的地图,然后降低可读性可能更有用。因此,我认为这取决于用例,您希望达到哪些目标。