这个action的作用:
在reduce中,是把rdd中所有的task中的结果合并成一个结果,最终输出这个结果.这个类似于一个count操作.在reduce中,传入的函数传入两个T类型的参数(T类型是RDD中KV的类型),这个函数返回值也是一个T类型的结果.注意,很多同学会把这个跟hadoop中的map reduce中的reduce进行等同看待,其实不是的,在map reduce中的reduce更像是spark中的一个groupByKey的操作,spark中的reduce只返回一个值,这个值是执行REDUCE操作的RDD中KV的类型.
在reduce的实现内部,定义了两个函数来进行处理.
def reduce(f: (T, T) => T): T = withScope {
1,这里定义了每个partition的task的执行函数,主要是对iterator进行迭代,
1,先计算f(a1,a2) ,其中f是一个函数,得到一个T类型的值tmpa..
2,接着根据迭代,计算 tmpa与a3的值,得到一个T类型的值tmpa,,,以后按这个方法一直做,直到iterator迭代完成.
val cleanF = sc.clean(f)
val reducePartition: Iterator[T] => Option[T] = iter => {
if (iter.hasNext) {
Some(iter.reduceLeft(cleanF))
} else {
None
}
}
这个函数是driver端的resultHandler函数,用于把所有的task返回的T类型的值进行合并,合并的方法一样,还是通过上面定义的f函数来进行.
var jobResult: Option[T] = None
val mergeResult = (index: Int, taskResult: Option[T]) => {
if (taskResult.isDefined) {
jobResult = jobResult match {
case Some(value) => Some(f(value, taskResult.get))
case None => taskResult
}
}
}
这里是执行这个任务
sc.runJob(this, reducePartition, mergeResult)
最后,返回合并的T类型的值,所有的task都合并成一个值,如果这个值是null,会出错.也就是说,如果要执行reduce操作,那么这个RDD中必须是一个有数据集的RDD.
// Get the final result out of our Option, or throw an exception if the RDD was
empty
jobResult.getOrElse(throw new UnsupportedOperationException("empty collection"))
}
操作示例:
Rdd中存储的key的类型为用户名称,value是访问过的站点,现在要计算用户是张三这哥们的访问所有站点的总次数,只返回张三这哥们的总次数(这哥们比较NB),其它的用户不统计.
Rdd.filter(kv => kv._1 == 张三).map(kv => kv._1 -> 1).reduce((kv1,kv2) => {
kv1._2 += kv2._2
})