這篇文章画髓,是我在嘗試理解Spark的一些概念時掘剪,找到的一篇文章。這篇文章主要介紹的是Spark中的內(nèi)存模型雀扶。由于譯者水平有限杖小,翻譯的過程中難免有些錯誤,請讀者懷著質(zhì)疑的心理來閱讀這篇文章愚墓。
我們先來看 http://spark.apache.org/docs/1.3.0/cluster-overview.html中給的一副圖片:
正如你所看到的那樣予权,其中包含了太多術(shù)語。我個人不是很喜歡這種風格浪册,因為它并不能突出一些重點概念扫腺。
我們從頭開始說。在你的集群中村象,任何Spark進程都是都是一個JVM進程笆环。所以,你可以通過配置JVM的內(nèi)存的那些參數(shù)來配置它厚者。下面是一幅關(guān)于Spark如何分配JVM堆內(nèi)存的圖片:
默認情況下躁劣,Spark會啟動一個有512MB大小堆內(nèi)存的JVM。為了保證安全库菲,避免OOM這種錯誤账忘,Spark只允許你使用90%的堆內(nèi)存,當然,你可以通過spark.storage.safetyFraaction參數(shù)來進行調(diào)節(jié)鳖擒。
你可能聽說過這樣一種說法溉浙,作為一個內(nèi)存工具,Spark允許你將數(shù)據(jù)保存到內(nèi)存中蒋荚。如果你看過我的https://0x0fff.com/spark-misconceptions/這篇文章戳稽,你應該明白,Spark并不是一個真正的內(nèi)存工具期升,它只是按照LRU算法來使用的它的緩存惊奇。
所以,Spark會劃分一部分內(nèi)存吓妆,用于緩存你在處理過程中產(chǎn)生的數(shù)據(jù)赊时,這一部分內(nèi)存的大小,通常是安全堆(堆的90%)的60%行拢,你也可以通過spark.storage.memoryFraction參數(shù)來調(diào)節(jié)祖秒。所以,如果你想要知道舟奠,在你的集群中竭缝,Spark能夠緩存多少數(shù)據(jù),你可以通過下面的公式計算:集群中全部的堆的大小safetyFraction * storage.memoryFraction,在默認的情況下,允許Spark使用90%60%誊辉,也就是54%的集群中全部的堆內(nèi)存。
現(xiàn)在我們來介紹一下shuffle memory湿故。它可以通過下面的公式計算:堆大小 * spark.shuffle.safetyFraction * spark.shuffle.memoryFraction。其中膜蛔,默認情況下坛猪,spark.shuffle.memoryFraction是80%,spark.shuffle.memoryFraction是20%皂股。所以墅茉,你可以將16%的堆大小用于shuffle。
那么呜呐,shuffle memory是干嘛的呢就斤?顧名思義,一般情況下蘑辑,就是用于shuffle這個操作洋机。當執(zhí)行shuffle操作時,有的時候洋魂,需要對數(shù)據(jù)進行排序绷旗,這時候啄踊,我們就需要一個buffer,而shuffle memory就可以作為這個buffer來使用刁标。那么,如果我們的數(shù)據(jù)量太大址晕,shuffle memory不夠用怎么辦膀懈?我們有大量的外排序算法可以使用。
還有一塊區(qū)域我們沒有介紹到谨垃,就是unroll memory启搂。這塊內(nèi)存的大小可以通過下面的公式計算:堆大小 * spark.storage.unrollFraction * spark.storage.memoryFraction * spark.storage.safetyFraction,默認情況下刘陶,就是20% * 60% * 90%胳赌,即10.8%。
那么匙隔,unroll memory又是干嘛的呢疑苫?這塊內(nèi)存,是我們需要數(shù)據(jù)"展開"的時候使用的纷责。比如捍掺,被序列化之后的數(shù)據(jù)并不能被直接使用,所以再膳,我們需要將它"展開"挺勿。這塊區(qū)域是和Storage這塊區(qū)域共享的,所以喂柒,這也就意味著不瓶,如果你需要一些內(nèi)存來展開數(shù)據(jù),那么灾杰,可能會丟失一些存儲在Spark LRU Cache中的Partition.
現(xiàn)在你理解了Spark如何使用內(nèi)存了蚊丐。
那我們來看一下,當你啟動一個Spark集群時吭露,它看起來是什么樣子吠撮?下面的例子都是針對YARN來進行的。
在一個YARN集群中讲竿,有一個Resource Manager,用于控制集群中的資源泥兰,以及好多個Node Managers,用來控制每個節(jié)點上資源的使用率题禀。當你向YARN Resource Manager請求資源的時候鞋诗,它會告訴你,你應該去聯(lián)系哪個Node Manager迈嘹,來獲取execution container削彬。每個execution container都是一個擁有你請求的資源的JVM全庸。execution container的位置是由Resource Manager決定的,你無法控制它融痛。比如壶笼,如果一個節(jié)點有64GB的內(nèi)存,然后你請求10個executors雁刷,每個executors都需要4GB的內(nèi)存覆劈,那么,即使你有一個包含很多個節(jié)點的集群沛励,這些executors還是可能僅僅運行在一個節(jié)點上责语。
你的Job,會被拆分成不同的Stage目派,然后坤候,每個Stage又會被切分成tasks。每個task都會被分開調(diào)度企蹭。你可以把每個executor看成一個task池白筹,每個executor都可以讓你運行spark.executor.cores / spark.task.cpus個tasks。
例如练对,如果我們有一個12個節(jié)點的集群遍蟋,每個節(jié)點都有64GB的內(nèi)存,32個核螟凭。所以虚青,你可以在每個節(jié)點上啟動兩個用于26GB內(nèi)存以及12個核的executor。所以你的集群總共能處理12 machines * 2 executors per machine * 12 cores per executor / 1 core for each task = 288個task螺男。也就是說棒厘,你的集群能夠同時運行288個task。這個集群中下隧,你可以用來緩存你的數(shù)據(jù)的內(nèi)存為0.9 spark.storage.safetyFraction * 0.6 spark.storage.memoryFraction * 12 machines * 2 executors per machine * 26GB per executor=336.96GB奢人。