我已经读过某个地方,对于作用于单个RDD的操作,例如reduceByKey()
,在预分区的RDD上运行将导致每个键的所有值在本地计算机器,只需要从每个工作节点发送最终的本地减少的值回到主机。这意味着我必须声明一个像:
val sc = new SparkContext(...)
val userData = sc.sequenceFile[UserID, UserInfo]("hdfs://...")
.partitionBy(new HashPartitioner(100)) // Create 100 partitions
.persist()
以便reduceByKey
按照我之前的解释工作。
我的问题是,如果我想使用reduceByKey(最佳),我是否需要在每次分区时声明或者不需要声明。
答案 0 :(得分:2)
实际上,你所谈论的两个品质有点不相关。
对于reduceByKey()
,第一个质量会聚合同一个键的元素,并在每个执行器上使用提供的关联reduce函数本地优先,然后最终在执行程序之间进行聚合。它封装在一个名为mapSideCombine
的布尔参数中,如果设置为true则执行上述操作。如果设置为false,与groupByKey()
一样,则每个记录将被洗牌并发送给正确的执行者。
第二个质量涉及分区及其使用方式。根据其定义,每个RDD包含分裂列表和(可选地)分区器。方法reduceByKey()
被重载,实际上有一些定义。例如:
def reduceByKey(func: (V, V) => V): RDD[(K, V)]
此方法的定义实际上使用来自父RDD的默认现有分区程序,并减少为设置为默认并行级别的分区数。
def reduceByKey(func: (V, V) => V, numPartitions: Int): RDD[(K, V)]
该方法的定义将使用HashPartitioner
将适当的数据提供给相应的执行者,分区数量为numPartitions
。
def reduceByKey(partitioner: Partitioner, func: (V, V) => V): RDD[(K, V)]
最后,该方法的这个定义取代了另外两个,并且接受了一个通用(可能是自定义)分区器,它将产生由分区器如何分区键确定的分区数。
关键是你可以在reduceByKey()
本身内实际编码所需的分区逻辑。如果你的意图是通过预分区避免改变开销,那么它也没有意义,因为你仍然会在你的预分区上进行洗牌。