Scala 为什么Spark系列中没有“reduceByValue”?
我正在学习Spark和Scala,并不断遇到这种模式:Scala 为什么Spark系列中没有“reduceByValue”?,scala,apache-spark,Scala,Apache Spark,我正在学习Spark和Scala,并不断遇到这种模式: val lines = sc.textFile("data.txt") val pairs = lines.map(s => (s, 1)) val counts = pairs.reduceByKey((a, b) => a + b) 虽然我了解它的功能,但我不明白为什么使用它而不是像: val lines = sc.textFile("data.txt") val counts = lines.reduceByValue(
val lines = sc.textFile("data.txt")
val pairs = lines.map(s => (s, 1))
val counts = pairs.reduceByKey((a, b) => a + b)
虽然我了解它的功能,但我不明白为什么使用它而不是像:
val lines = sc.textFile("data.txt")
val counts = lines.reduceByValue((v1, v2) => v1 + v2)
鉴于Spark旨在高效地处理大量数据,因此总是需要执行一个附加步骤,将列表转换为地图,然后按键进行缩减,而不是简单地按值进行缩减,这似乎是违反直觉的。首先,这个“附加步骤”的成本并不高(请参阅最后的更多详细信息)-它不洗牌数据,并且与其他转换一起执行:只要转换不改变分区,就可以“流水线”转换
其次-您建议的API似乎非常适合于计数-尽管您建议reduceByValue
将采用二进制运算符f:(Int,Int)=>Int
,您建议的API假设每个值都映射到值1
,然后再将此运算符应用于所有相同的值-这一假设在除计数之外的任何场景中几乎没有用处。添加这样的特定API只会使接口膨胀,而且无论如何也不会覆盖所有用例(下一步是什么?RDD.wordCount
),因此最好为用户提供最小的构建块(以及良好的文档)
最后—如果您不满意这些低级API,可以使用Spark SQL的DataFrame API获得一些更高级的API,这些API将隐藏这些细节—这就是DataFrames存在的原因之一:
val linesDF = sc.textFile("file.txt").toDF("line")
val wordsDF = linesDF.explode("line","word")((line: String) => line.split(" "))
val wordCountDF = wordsDF.groupBy("word").count()
编辑:根据要求-有关此映射操作的性能影响很小或完全可以忽略的原因的更多详细信息:
- 首先,我假设您有兴趣生成与map->reduceByKey代码生成的结果相同的结果(即字数),这意味着从每个记录到值
的映射必须在某个地方进行,否则就没有什么东西可以执行求和函数1
(该函数需要(v1,v2)=>v1+v2
s,它们必须在某处创建)Int
- 据我所知,你只是想知道为什么这必须作为一个单独的映射操作发生
- 因此,我们实际上对添加另一个映射操作的开销感兴趣
val rdd: RDD[String] = ???
/*(1)*/ rdd.map(s => s.length * 2).collect()
/*(2)*/ rdd.map(s => s.length).map(_ * 2).collect()
Q:哪一个更快?A:它们的性能相同 为什么?因为只要RDD上的两个连续转换不改变分区(在您的原始示例中也是如此),Spark就会将它们组合在一起,并在同一个任务中执行它们。因此,根据记录,这两个转换之间的差异将归结为:
/*(1)*/ s.length * 2
/*(2)*/ val r1 = s.length; r1 * 2
这一点可以忽略不计,尤其是当您讨论大型数据集上的分布式执行时,执行时间主要由洗牌、反序列化和IO等操作控制。首先,这一“附加步骤”实际上成本不高(请参阅最后的更多详细信息)-它不洗牌数据,并且与其他转换一起执行:只要转换不改变分区,就可以“流水线”转换
第二,您建议的API似乎非常适合计数——尽管您建议reduceByValue
将采用二进制运算符f:(Int,Int)=>Int
,您建议的API假设每个值都映射到值1
,然后将此运算符应用于所有相同的值-这一假设在除计数之外的任何场景中都几乎没有用处。添加这样的特定API只会使接口膨胀,而且永远不会覆盖所有用例(下一步是什么?RDD.wordCount
),因此最好为用户提供最小的构建块(以及良好的文档)
最后—如果您不满意这些低级API,可以使用Spark SQL的DataFrame API获得一些更高级的API,这些API将隐藏这些细节—这就是DataFrames存在的原因之一:
val linesDF = sc.textFile("file.txt").toDF("line")
val wordsDF = linesDF.explode("line","word")((line: String) => line.split(" "))
val wordCountDF = wordsDF.groupBy("word").count()
编辑:根据要求-有关此映射操作的性能影响很小或完全可以忽略的原因的更多详细信息:
- 首先,我假设您有兴趣生成与map->reduceByKey代码生成的结果相同的结果(即字数),这意味着从每个记录到值
的映射必须在某个地方进行,否则就没有什么东西可以执行求和函数1
(该函数需要(v1,v2)=>v1+v2
s,它们必须在某处创建)Int
- 据我所知,你只是想知道为什么这必须作为一个单独的映射操作发生
- 因此,我们实际上对添加另一个映射操作的开销感兴趣
val rdd: RDD[String] = ???
/*(1)*/ rdd.map(s => s.length * 2).collect()
/*(2)*/ rdd.map(s => s.length).map(_ * 2).collect()
Q:哪一个更快?A:它们的性能相同 为什么?因为只要RDD上的两个连续转换不改变分区(在您的原始示例中也是如此),Spark就会将它们组合在一起,并在同一个任务中执行它们。因此,根据记录,这两个转换之间的差异将归结为:
/*(1)*/ s.length * 2
/*(2)*/ val r1 = s.length; r1 * 2
这是可以忽略的,特别是当您讨论大型数据集上的分布式执行时,其中执行时间主要由洗牌、反序列化和IO等操作控制。您希望该逻辑做什么?有
rdd.reduce()
。它只是不叫reduceByValue
。你不需要做额外的映射。所以你可以只做-行。reduce((v1,v2)=>v1+v2)
@SarveshKumarSingh-谢谢,这正是我想要的答案。@SarveshKumarSingh行。reduce((