在这个action的操作中:
Take的操作主要用于取出前n条数据的操作,这个操作中首先从第一个partition中去找,如果第一个partition中无法取出前n条数据时,再接下来从后面的partition中接着去找.直到取到n条数据或者说partition中都已经找完.与其对应的还有takeOrder的操作,这个后面在说.
发起JOB的过程:
1,如果take的操作是0,那么直接返回,job都不启动.这里面好像没有对值是负数的判断.
if (num == 0) {
new Array[T](0)
}
2,如果take的操作要返回的值是不一个不是0的值,这个地方准确的说是一个大于0的值.
先生成一个用于存储结果的buffer,
val buf = new ArrayBuffer[T]
最大可能需要读取的partition的个数.
val totalParts = this.partitions.length
开始准备要进行读取的一个partition,默认从第0个开始.
var partsScanned = 0
3,进行迭代,这个迭代的结束条件是take到指定的条数后,或者说要查找的partition已经达到了所有的partition的个数.
while (buf.size < num && partsScanned < totalParts) {
}
3,1,这个过程在迭代的内部进行,默认情况下,第一次读取一个partition的结果,
var numPartsToTry = 1
如果说已经读取了一个partition的结果,但是这个结果的数据目前还不满足take要的number的条数.
if (partsScanned > 0) {
if (buf.size == 0) {
如果读取完第一个,或者说最少读取了第一个partition的结果,但是take的结果目前还是0条,那么一次读取,就读取原来读取的partition个数的4倍.
numPartsToTry = partsScanned * 4
} else {
这种情况下,上次的take从指定的部分partition中已经读取到了部分数据,但这部分数据还不满足要求.
这里根据当前的partition的下标,与要查找的条数,*1.5再与当前已经读取到的buffer的条数进行相除.
这里用于计算下一次需要读取的partition的个数.
numPartsToTry = Math.max((1.5 * num * partsScanned / buf.size).toInt
- partsScanned, 1)
numPartsToTry = Math.min(numPartsToTry, partsScanned * 4)
}
}
这里计算出要读取的剩余的条数.
val left = num - buf.size
得到要读取的几个partition的个数.,这个从一个开始的partition开始读取,
第一次是,从第0个partition开始读取,第一次时,读取一个partition,也就是numPartsToTry是1.
val p = partsScanned until math.min(partsScanned + numPartsToTry, totalParts)
根据要读取的partition的下标集合,在每个partition中执行iterator的take的操作.
这个take操作通过还需要读取的剩余条数得到数据的数组,这是一个迭代的过程,直到读取到指定的条数或者partition已经读取完成.
这里发起在每个task中记录的function为it: Iterator[T]) => it.take(left).toArray.
Driver端resulthandler的function与collect的操作一样,定义一个数组,把每个task的结果存储到对应的位置上.
val res = sc.runJob(this, (it: Iterator[T]) => it.take(left).toArray, p)
把读取到的数据添加到buffer中.
res.foreach(buf ++= _.take(num - buf.size))
读取下一次要开始读取的partition的下标.
partsScanned += numPartsToTry
最后程序结束时的返回值,在while迭代外部:
buf.toArray