有两种方法实现wordcount,一种是使用reduceByKey,另一种是使用groupByKey。
val words = Array("one", "two", "two", "three", "three", "three")
val wordPairsRDD = sc.parallelize(words).map(word => (word, 1))
val wordCountsWithReduce = wordPairsRDD
.reduceByKey(_ + _)
.collect()
val wordCountsWithGroup = wordPairsRDD
.groupByKey()
.map(t => (t._1, t._2.sum))
.collect()
虽然两种方式都能得到正确的结果,但是在大规模数据集上,使用reduceByKey的性能更好。这是因为Spark知道在shuffle数据之前在每个partition上对数据按照统一的key进行结合。
reduceByKey的过程如下图所示:
可以看出shuffle过程的数据有(a,1),(b,1),(a,2),(b,2),(a,3),(b,3)
然而,在使用groupByKey的时候,所有的key-value对都被shuffle了。导致了大量不必要的网络传输。
要决定一个数据对被shuffle到哪台机器上,Spark会在数据对的key上调用分区函数。当shuffle后的数据在内存中放不下的时候,Spark会将其spill到硬盘上。然而,它每次把一个key的所有数据存到硬盘,所以如果某一个key的所有键值对在内存中存不下了,就会产生OOM异常。
groupByKey的工作过程如下:
而上面的groupByKey的过程中,shuffle传输的数据是rdd中所有的元素,因此性能很差。
可以想象当对于大数据集,两个方法之间的性能差异会有多么夸张。
另外两个优于groupByKey的函数:
combineByKey:can be used when you are combining elements but your return type differs from your input value type.
foldByKey:merges the values for each key using an associative function and a neutral "zero value".
这两个函数的具体用法可以查看另一篇文章:
《Spark算子:RDD键值转换操作(2)–combineByKey、foldByKey》http://blog.csdn.net/sdujava2011/article/details/50598481
内容整理自https://databricks.gitbooks.io/databricks-spark-knowledge-base/content/best_practices/prefer_reducebykey_over_groupbykey.html,所有权力归原作者所有。