Spark:reduce和reduceByKey之间的语义差异

11
在Spark的文档中,它说RDDs方法reduce需要一个满足结合律和交换律的二元函数。
然而,方法reduceByKey仅需要一个满足结合律的二元函数。
sc.textFile("file4kB", 4)

我做了一些测试,显然这是我得到的行为。为什么会有这种差异?为什么reduceByKey可以确保二元函数始终按照特定顺序应用(以适应缺乏可交换性的情况),而reduce则不能?
例如,如果加载一些(小)文本并将其分为4个分区(最小值):
val r = sc.textFile("file4k", 4)

然后:

r.reduce(_ + _)

返回一个字符串,其中的部分不总是按相同的顺序排列,而:
r.map(x => (1,x)).reduceByKey(_ + _).first

始终返回相同的字符串(其中所有内容与原始文件中的顺序相同)。
(我使用进行了检查,文件内容确实分布在4个分区上,没有空分区)。

2
我猜 reduceByKey 的想法是你可能有很多不同的键,所以将单个键的所有内容缩减到单个线程上是可以的,这意味着您始终可以从左到右运行计算。相比之下,reduce 经常用于大型数据集,因此不必关心操作顺序。 - Rex Kerr
你在实验中使用了多少个执行器? - gprivitera
2个回答

7
就我而言,我认为这是文档中的错误,你看到的结果只是偶然的。实践、其他资源和对代码的简单分析表明传递给reduceByKey的函数不仅应该是可结合的,而且还应该是可交换的。
  • 练习 - 虽然在本地模式下看起来顺序得以保留,但在运行包括独立模式在内的 Spark 集群时,这一点不再成立。

  • 其他资源 - 引用自 AmpCamp 3使用 Spark 进行数据探索

    Spark 中有一个方便的方法叫做 reduceByKey ,可完美应对此类模式。请注意,reduceByKey 的第二个参数决定要使用的 reducer 数量。默认情况下,Spark 假定 reduce 函数是可交换和可结合的,并在 mapper 端应用 combiner。

  • 代码 - reduceByKey 是使用 combineByKeyWithClassTag 实现的,创建了 ShuffledRDD。由于 Spark 在洗牌后不保证顺序,恢复它的唯一方法是将某些元数据附加到部分减少的记录上。据我所知,没有发生这样的事情。

顺便提一下,在PySpark中实现的reduce函数可以很好地处理仅满足交换律的函数。当然,这只是实现的细节,而不是合约的一部分。


3
我会尽力进行翻译。以下是需要翻译的内容:I'd add that reduce is an action, returning data to driver, while reduceByKey is a transformation, returning another RDD.我的翻译如下:我认为需要补充说明,reduce是一种操作,将数据返回给驱动程序,而reduceByKey是一种转换,返回另一个RDD。 - rhernando
谢谢!但是,Spark中是否有一种方法来确保非交换处理的正确性?还是说这超出了Spark的范围? - Yves Parès
我不确定是否理解问题。您是在询问是否可以自动测试/证明可交换性,还是只想使用非交换函数与reduce?如果是后一种情况,模仿PySpark行为(mapPartitions(reduceFunc)=> collect => reduce(reduceFunc)`)应该可以工作,但会有一些性能损失。 - zero323
谢谢您的提示。你是说PySpark的reduce有顺序行为吗?为什么会有这种区别? - Yves Parès
旁注:看起来你在依赖collect返回一个按原始顺序分区的数组。我也观察到了这种行为,但它没有被记录在文档中。这是一种可靠的行为吗? - Yves Parès
鉴于当前的代码库,它是不太可能改变的。有不同的Spark代码片段依赖于这种行为。关于PySpark行为的差异,我不是开发人员,但我的打赌是这只是工具问题。Python直到3.4-3.5版本都没有提供体面的异步原语。此外,如果reduce函数不是非常昂贵的话,也没有太多可获得的东西。 - zero323

1
根据最近更新/修正的代码文档(感谢@zero323):

reduceByKey使用可结合且可交换的reduce函数合并每个键的值。这也会在将结果发送到reducer之前在每个mapper上本地执行合并,类似于MapReduce中的“combiner”。

所以实际上是像@zero323在他的答案中指出的那样一个文档错误。
您可以检查以下链接以确保:

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接