這篇文章將為大家詳細(xì)講解有關(guān)Spark2.x中怎么實(shí)現(xiàn)CacheManager源碼深度剖析,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
為鎮(zhèn)巴等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計(jì)制作服務(wù),及鎮(zhèn)巴網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為成都做網(wǎng)站、網(wǎng)站建設(shè)、鎮(zhèn)巴網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!
一、概述
CacheManager主要發(fā)生在利用RDD的數(shù)據(jù)執(zhí)行算子的時(shí)候,之前我們講過在ShufffleWriter進(jìn)行數(shù)據(jù)寫時(shí),會(huì)調(diào)用RDD對(duì)應(yīng)的Iterator()方法,獲取RDD對(duì)應(yīng)的數(shù)據(jù),CacheManager主要干三件事:
a. 管理Spark的緩存,可以基于內(nèi)存,也可以基于磁盤;
b.底層是通過BlockManager進(jìn)行數(shù)據(jù)的讀寫操作;
c.Task運(yùn)行會(huì)調(diào)用RDD中的iterator方法進(jìn)行數(shù)據(jù)的計(jì)算;
二、CacheManager源碼剖析
1.之前我們講解的ShuffleMapTask中的runTask方法時(shí),ShuffleWriter寫數(shù)據(jù)的參數(shù)傳入的是rdd.iterator()方法計(jì)算出來的那個(gè)partition數(shù)據(jù),代碼如下:
var writer: ShuffleWriter[Any, Any] = null try { val manager = SparkEnv.get.shuffleManager writer = manager.getWriter[Any, Any](dep.shuffleHandle, partitionId, context) //這里就是ShuffleMapTask類的runTask()方法中對(duì)應(yīng)的代碼調(diào)用 writer.write(rdd.iterator(partition, context).asInstanceOf[Iterator[_ <: Product2[Any, Any]]]) writer.stop(success = true).get } catch { ................... }
2.這里看RDD類中的iterator方法,代碼如下:
final def iterator(split: Partition, context: TaskContext): Iterator[T] = {
//判斷下如果StorageLevel.NONE這說明RDD,之前肯定是進(jìn)行了持久化
//getOrCompute中會(huì)通過CacheManager獲取之前持久化的數(shù)據(jù)
if (storageLevel != StorageLevel.NONE) {
getOrCompute(split, context)
//如果沒有進(jìn)行過持久化,就需要通過父RDD定義的算子去獲取數(shù)據(jù)
//注意這里如果有CheckPoint,會(huì)通過CheckPoint獲取,checkPoint獲取不到才去重新計(jì)算
} else {
computeOrReadCheckpoint(split, context)
}
}
3.跟進(jìn)去看下持久化的RDD的處理,getOrCompute()函數(shù),代碼如下:
private[spark] def getOrCompute(partition: Partition, context: TaskContext): Iterator[T] = { val blockId = RDDBlockId(id, partition.index) var readCachedBlock = true //CacheManger這里是通過BlockManager獲取持久化數(shù)據(jù), //如果獲取成功直接返回,如果獲取失敗,調(diào)用computeOrReadCheckpoint進(jìn)行計(jì)算 //內(nèi)存數(shù)據(jù)為啥會(huì)丟失? 之前我們知道內(nèi)存中的數(shù)據(jù)如果空間不夠的話,同時(shí)如果指定可以將數(shù)據(jù)緩存到磁盤,會(huì)溢寫到磁盤, //如果未指定溢寫到磁盤,這些數(shù)據(jù)就會(huì)丟失掉 就需要重新計(jì)算 SparkEnv.get.blockManager.getOrElseUpdate(blockId, storageLevel, elementClassTag, () => { readCachedBlock = false //獲取不到重新計(jì)算,這里要注意,代碼執(zhí)行到這里說明這個(gè)RDD肯定是經(jīng)過持久化的 //這里計(jì)算出數(shù)據(jù)后,會(huì)在getOrElseUpdate里面通過makeIterator參數(shù)對(duì)數(shù)據(jù)進(jìn)行重新持久化(這里理解的不太透徹) computeOrReadCheckpoint(partition, context) }) match { case Left(blockResult) => if (readCachedBlock) { val existingMetrics = context.taskMetrics().inputMetrics existingMetrics.incBytesRead(blockResult.bytes) new InterruptibleIterator[T](context, blockResult.data.asInstanceOf[Iterator[T]]) { override def next(): T = { existingMetrics.incRecordsRead(1) delegate.next() } } } else { new InterruptibleIterator(context, blockResult.data.asInstanceOf[Iterator[T]]) } case Right(iter) => new InterruptibleIterator(context, iter.asInstanceOf[Iterator[T]]) } }
4.這里繼續(xù)跟蹤getOrElseUpdate()獲取持久化的數(shù)據(jù) ,代碼如下:
//這里會(huì)調(diào)用get()方法從本地或者遠(yuǎn)程獲取block數(shù)據(jù),直接返回//如果沒有讀取到數(shù)據(jù)就需要重新計(jì)算數(shù)據(jù),由于代碼執(zhí)行到這里,rdd肯定是經(jīng)過持久化的//這里計(jì)算出數(shù)據(jù)后,通過makeIterator迭代器,重新進(jìn)行持久化(這里理解的不太透徹) def getOrElseUpdate[T]( blockId: BlockId, level: StorageLevel, classTag: ClassTag[T], makeIterator: () => Iterator[T]): Either[BlockResult, Iterator[T]] = { // Attempt to read the block from local or remote storage. If it's present, then we don't need // to go through the local-get-or-put path. //這里會(huì)調(diào)用get()方法從本地或者遠(yuǎn)程獲取block數(shù)據(jù),直接返回 get[T](blockId)(classTag) match { case Some(block) => return Left(block) case _ => // Need to compute the block. } //這里的處理意思是:對(duì)于本地遠(yuǎn)程沒有獲取到數(shù)據(jù),然后computeOrReadCheckpoint重新計(jì)算的數(shù)據(jù) //由于RDD是持久化的,原來的持久化數(shù)據(jù)可能丟了,這里根據(jù)持久化級(jí)別重新進(jìn)行數(shù)據(jù)的持久化 //這里代碼有點(diǎn)不太好理解 要結(jié)合上面2中第12-14行代碼 一起理解 doPutIterator(blockId, makeIterator, level, classTag, keepReadLock = true) match { case None => // doPut() didn't hand work back to us, so the block already existed or was successfully // stored. Therefore, we now hold a read lock on the block. val blockResult = getLocalValues(blockId).getOrElse { // Since we held a read lock between the doPut() and get() calls, the block should not // have been evicted, so get() not returning the block indicates some internal error. releaseLock(blockId) throw new SparkException(s"get() failed for block $blockId even though we held a lock") } // We already hold a read lock on the block from the doPut() call and getLocalValues() // acquires the lock again, so we need to call releaseLock() here so that the net number // of lock acquisitions is 1 (since the caller will only call release() once). releaseLock(blockId) Left(blockResult) case Some(iter) => // The put failed, likely because the data was too large to fit in memory and could not be // dropped to disk. Therefore, we need to pass the input iterator back to the caller so // that they can decide what to do with the values (e.g. process them without caching). Right(iter) } }
5.這里回過頭來看computeOrReadCheckpoint方法,如果計(jì)算數(shù)據(jù)的,代碼如下:
private[spark] def computeOrReadCheckpoint(split: Partition, context: TaskContext): Iterator[T] = { //如果設(shè)置了CheckPoint,從Checkpoint中獲取數(shù)據(jù) //這里CheckPoint相關(guān)的知識(shí),先不講解,后面有篇文章單獨(dú)講解 if (isCheckpointedAndMaterialized) { firstParent[T].iterator(split, context) } else { //如果數(shù)據(jù)沒有進(jìn)行過Checkpoint,這里只能重新計(jì)算一次 //這里就是根據(jù)自己的rdd算子重新計(jì)算 compute(split, context) } }
6.CacheManager數(shù)據(jù)計(jì)算的大體流程:
1).如果RDD進(jìn)行過持久化,根據(jù)持久化級(jí)別通過BlockManager從本地或者遠(yuǎn)程獲取數(shù)據(jù),如果數(shù)據(jù)獲取不到,則需要重新計(jì)算,由于這里RDD進(jìn)行過持久化,只是由于某種原因丟失,還需要根據(jù)持久化級(jí)別重新進(jìn)行一次數(shù)據(jù)的持久化。
2).如果RDD沒有進(jìn)行持久化,就需要重新計(jì)算,重新計(jì)算時(shí),這里如果RDD進(jìn)行了CheckPoint,則優(yōu)先獲取CheckPoint過的數(shù)據(jù),如果沒有,則需要從RDD的父RDD執(zhí)行我們定義的算子來重新計(jì)算Partition數(shù)據(jù)。
關(guān)于Spark2.x中怎么實(shí)現(xiàn)CacheManager源碼深度剖析就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。