丨內(nèi)存視角三oom都是誰的鍋怎么破_第1頁
丨內(nèi)存視角三oom都是誰的鍋怎么破_第2頁
丨內(nèi)存視角三oom都是誰的鍋怎么破_第3頁
丨內(nèi)存視角三oom都是誰的鍋怎么破_第4頁
丨內(nèi)存視角三oom都是誰的鍋怎么破_第5頁
已閱讀5頁,還剩9頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

那么,當我們拿到OOM這個“燙手的山芋”的時候該怎么辦呢?我們最先應該弄清楚的是“到底哪里出現(xiàn)了OOM”。只有準確定位出現(xiàn)問題的具體區(qū)域,我們的調(diào)優(yōu)才能有的放矢。具體來說,這個“哪里”,我們至少要分3個方面去看。發(fā)生OOMLOC(LineOfCode),也就是代碼位置在哪?OOM發(fā)生在Driver端,還是在Executor端?如果是發(fā)生在Executor端,OOM到底發(fā)生在哪一片內(nèi)存區(qū)定位出錯代碼的位置非常重要但也非常簡單,我們只要利用StackTrace就能很快找到拋出問題的LOC。因此,更關鍵的是,我們要明確出問題的到底是Driver端還是Executor端,以及是哪片內(nèi)存區(qū)域。Driver和Executor產(chǎn)生OOM的病灶不同,我們自然需要區(qū)所以今天這一講,我們就先來說說Driver端的OOM問題和應對方法。由于內(nèi)存在Executor端被劃分成了不同區(qū)域,因此,對于Executor端怪相的OOM,我們還要結合案例來分類討論。最后,我會帶你整理出一套應對OOM“武功秘籍”,讓你在面對OOM的時候,能夠見招拆招、有的放矢!Driver端的我們先來說說Driver的OOM。Driver主要職責是任務調(diào)度,同時參與非常少量的任務計算,因此Driver的內(nèi)存配置一般都偏低,也沒有更加細分的內(nèi)存區(qū)域。因為Driver內(nèi)存就是囫圇的那么一塊Driver的OOM題自然不是調(diào)度系統(tǒng)創(chuàng)建小規(guī)模的分布式數(shù)據(jù)集:使用parallelize、createDataFrameAPI建數(shù)據(jù)集收集計算結果:通過take、show、collect等算子把結果收集到Driver端因此Driver端的OOM逃不出2類病灶收集的結果集超過內(nèi)存上第一類病灶不言自明,咱們不細說了??吹降诙惒≡?,想必你第一時間想到的就是萬惡的collect。確實,說到OOM就不得不提llect。collect算子會從Eeutors把全量數(shù)據(jù)拉回到Drier端,因此,如果結果集尺寸超過Driver內(nèi)存上限,它自然會報OOM。由開發(fā)者直接調(diào)用collect算子而觸發(fā)的OOM問題其實很好定位,比較難定位的是間接調(diào)用collect而導致的OOM。那么,間接調(diào)用collect是指什么呢?還記得廣播變量的工廣播變量的創(chuàng)建與分廣播變量在創(chuàng)建的過程中,需要先把分布在所有Executors的數(shù)據(jù)分片拉取到Driver端,然后在Driver端構建廣播變量,最后Driver端把封裝好的廣播變量再分發(fā)給各個Executors。第一步的數(shù)據(jù)拉取其實就是用collect現(xiàn)的。如果Executors數(shù)據(jù)分片的總大小超過Driver端內(nèi)存上限也會報OOM。在日常的調(diào)優(yōu)工作中,你看到的表象和癥狀1java.lang.OutOfMemoryError:Notenoughmemorytobuildand但實際的病理卻是Driver端內(nèi)存受限,沒有辦法容納拉取回的結果集。找到了病因,再去應對Driver的OOM很簡單了。我們只要對結果集尺寸做適當?shù)念A估,然后再相應地增加Driver側的內(nèi)存配置就好了。調(diào)節(jié)Driver端側內(nèi)存大小我們要用到spark.driver.memory配置項,預估數(shù)據(jù)集尺寸可以用“先Cache,再查看執(zhí)行計劃”的1valdf:DataFrame=2323456789n=valestimated:BigInt= Executor我們再來說說Executor的OOM。我們知道,執(zhí)行內(nèi)存分為4區(qū)域Memory、UserMemory、StorageMemoryExecutionMemory。這4區(qū)域中都有哪些區(qū)域會報OOM異常呢?哪些區(qū)域壓根就不存在OOM的可能呢?Executors,與任務執(zhí)行有關的內(nèi)存區(qū)域才存在OOM隱患。其中Memory小固定為300MB,因為它是硬編碼到源碼中的,所以不受用戶控制。而對于StorageMemory來說,即便數(shù)據(jù)集不能完全緩存到MemoryStore,Spark也不會拋OOM異常,額外的數(shù)據(jù)要么落盤(MEMORY_AND_DISK)、要么直接放棄(MEMORY_ONLY)因此,當Executors出現(xiàn)OOM的問題,我們可以先把 Memory和StorageMemory排除,然后鎖定ExecutionMemory和UserMemory去找毛病。UserMemory的在內(nèi)存管理那一講,我們UserMemory用于用戶自定義的數(shù)據(jù)結構,如數(shù)組、列表、字典等。因此,如果這些數(shù)據(jù)結構的總大小超出了UserMemory內(nèi)存區(qū)域的上代代123java.lang.OutOfMemoryError:Javaheapspaceatjava.lang.OutOfMemoryError:Javaheapspaceat如果你的數(shù)據(jù)結構是用于分布式數(shù)據(jù)轉換,在計算UserMemory存消耗時,你就需要考慮Executor的線程池大小。還記得下面的這個例子嗎?代代1234valdict=List(“spark”,valwords==words.filter(word=>.map((_,1)).reduceByKey(_+自定義的列表dict會隨著Task分發(fā)到所有Executors,因此多個Task中的dict會對UserMemory產(chǎn)生重復消耗。如果把dict尺寸記為#size,Executor線程池大小記為#threads,那么dictUserMemory總消耗就是:#size*threads。一旦總消耗超出UserMemory內(nèi)存上限,自然就會產(chǎn)生OOM問題。用戶數(shù)據(jù)在任務那么,解決UserMemoryOOM思Driver的并無二致,也是先對數(shù)據(jù)結構的消耗進行預估,然后相應地擴大UserMemory的內(nèi)存配置。不過,相比Driver,UserMemory內(nèi)存上限的影響因素,總大小由spark.executor.memory*(1-ExecutionMemory的要說OOM高發(fā)區(qū),非ExecutionMemory屬。久行夜路必撞鬼,在分布式任務執(zhí)行的過程中,ExecutionMemory首當其沖,因此出錯的概率相比其他內(nèi)存區(qū)域更高。關于ExecutionMemory的OOM,我發(fā)現(xiàn)不少同學都存在這么一個誤區(qū):只要數(shù)據(jù)量比執(zhí)行內(nèi)存小就不會發(fā)生OOM,相反就會有一定的幾率觸發(fā)OOM問題。實際上,數(shù)據(jù)量并不是決定OOM與否的關鍵因素,數(shù)據(jù)分布與ExcutinMemry的運行時規(guī)劃是否匹配才是。這么說可能比較抽象,你還記得黃小乙的如意算盤嗎?為了提高老鄉(xiāng)們種地的熱情和積極性,他制定了個轉讓協(xié)議,所有老鄉(xiāng)申請的土地面積介于1/N2和1/N之間。因此,如果有的老鄉(xiāng)貪多求快,買的遠遠超過1/N上限能夠容納的數(shù)量,這位老鄉(xiāng)多買的那部分都會被白白浪費掉。同樣的,我們可以把ExecutionMemory看作土地,把分布式數(shù)據(jù)集看作是,一旦分布式任務的內(nèi)存請求超出1/N這個上限,ExecutionMemory就會出現(xiàn)OOM問題。而且,相比其他場景下的OOM問題,ExecutionMemoryOOM復雜得多,它不僅僅與內(nèi)存空間大小、數(shù)據(jù)分布有關,還與Executor線程池和運行時任務調(diào)度有關。抓住了引起OOM問題最的原因,對于EecuionMemoryOOM的諸多表象,我們就能從容應對了。下面,我們就來看兩個平時開發(fā)中常見的實例:數(shù)據(jù)傾斜和數(shù)據(jù)膨脹。為了方便說明,在這兩個實例中,計算節(jié)點的硬件配置是一樣的,都是2個CPUore,每個core有兩個線程,內(nèi)存大小為1GB,并且spar.execuor.cores設置為3,spar.eecutor.memory設置為900MB。ExecutionMemoryStorageMemory存空間都是180MB。而且,因為我們的例子里沒有RDD緩存,所以ExecutionMemory內(nèi)存空間上限是360MB。1:數(shù)據(jù)傾我們先來看第一個數(shù)據(jù)傾斜的例子。節(jié)點在Reduce階段拉取數(shù)據(jù)分片,3個ReduceTask對應的數(shù)據(jù)分片大小分別是100MB和300MB。顯然,第三個數(shù)據(jù)分片存在輕微的數(shù)據(jù)傾斜。由于Executor線程池大小為3,因此每個ReduceTask多可獲得360MB*13120MB內(nèi)存空間。Task1、Task2取到的內(nèi)存空間足以容納分片1、分片2,數(shù)據(jù)傾斜導致Task3數(shù)據(jù)分片大小遠超內(nèi)存上限,即便SparkReduce段Spill外排,120MB的內(nèi)存空間也300MB數(shù)據(jù)最基本的計算需要,如PairBuffer和AppendOnlyMap等數(shù)據(jù)結構的內(nèi)存消耗,以及數(shù)據(jù)排序的臨時內(nèi)存消耗等等。這個例子的表象是數(shù)據(jù)傾斜導致OOM,但實質(zhì)上是Task3內(nèi)存請求超出1/N此,針對以這個案例為代表的數(shù)據(jù)傾斜問題,我們至少有2種調(diào)優(yōu)思路:消除數(shù)據(jù)傾斜,讓所有的數(shù)據(jù)分片尺寸都不大于調(diào)整Executor線程池、內(nèi)存、并行度等相關配置,提高1/N上限到每一種思路都可以衍生出許多不同的方法,就拿第2思路來說,要滿足1/N上限,最簡單地,我們可以把spark.executor.cores設置成1,也就是Executor線程池只有一個線程“并行”工作。這個時候,每個任務的內(nèi)存上限都變成了360MB,容納300MB的數(shù)當然,線程池大小設置為1是不可取的,剛剛只是為了說明調(diào)優(yōu)的靈活性。延續(xù)第二個思路,你需要去平衡多個方面的配置項,在充分利用CPU的前提下解決OOM的問題。比維持并發(fā)度、并行度不變,增大執(zhí)行內(nèi)存設置,提高1/N上限到分片尺寸都縮小到100MB以內(nèi)關于線程池、內(nèi)存和并行度之間的平衡與設置,我在CPU視角那一講做過詳細的介紹,你2:數(shù)據(jù)膨我們再來看第二個數(shù)據(jù)膨脹的例子。節(jié)點在Map階段拉取HDFS數(shù)據(jù)分片,3個MapTask對應的數(shù)據(jù)分片大小都是100MB。按照之前的計算,每個MapTask最多可獲得120MB的執(zhí)行內(nèi)存,不應該出現(xiàn)OOM問題才對。數(shù)據(jù)膨脹導致尷尬的地方在于,磁盤中的數(shù)據(jù)進了JVM之后會膨脹。在我們的例子中,數(shù)據(jù)分片加載到JVMHeap之后翻了3倍,原本100MB的數(shù)據(jù)變成了300MB,因此,OOM就成了一在這個案例中,表象是數(shù)據(jù)膨脹導致OOM,但本質(zhì)上還是Task2和Task3的內(nèi)存請求超出1/N上限。因此,針對以這個案例為代表的數(shù)據(jù)膨脹問題,我們還是有至少2種調(diào)優(yōu)思把數(shù)據(jù)打散,提高數(shù)據(jù)分片數(shù)量、降低數(shù)據(jù)粒度,讓膨脹之后的數(shù)據(jù)量降到100MB加大內(nèi)存配置,結合Executor線程池調(diào)整,提高1/N上限到想要高效解決五花八門的OOM題,最重要的就是準確定位問題出現(xiàn)的區(qū)域,這樣我們首先,定位OOM發(fā)生的代碼位置,你通過StackTrace就能很快得到答其次,定位OOM發(fā)生在Driver還是在Executor。如果是發(fā)生在Executor端,發(fā)生在Driver端的OOM可以歸結為兩類:收集的結果集超過內(nèi)存上應對Driver端OOM的常規(guī)方法,是先適當預估結果集尺寸,然后再相應增加Driver側發(fā)生在Executors側的OOM只和UserMemory和ExecutionMemory區(qū)域有關,因為它們都和任務執(zhí)行有關。其中,UserMemory區(qū)域OOM的產(chǎn)生的原因和解決辦法與Driver別無二致,你可以直接參考。ExecutionMemory域OOM產(chǎn)生的原因是數(shù)據(jù)分布與ExecutionMemory運行時規(guī)劃不匹配,也就是分布式任務的內(nèi)存請求超出了1/N上限。解決ExecutionMemory區(qū)域OOM問題的思路總的來說可以分為3類:消除數(shù)據(jù)傾斜,讓所有的數(shù)據(jù)分片尺寸都小于1/N上限把數(shù)據(jù)打散,提高數(shù)據(jù)分片數(shù)量、降低數(shù)據(jù)粒度,讓膨脹之后的數(shù)據(jù)量降到1/N加大內(nèi)存配置,結合Executor線程池調(diào)整,提高1/N上限數(shù)據(jù)膨脹導致OOM例子中,為什么Task1獲取到300MB內(nèi)存空間?(提示:可以回顧CPU視角那一講去尋找答案。)在日常開發(fā)中,你還遇到過哪些OOM你能把它們歸納到我們今天講的分類中期待在留言區(qū)看到你的思考和,我們下一講見 歸科技所有 不得售賣。頁面已增加防盜追蹤,將依 上一 16|內(nèi)存視角(二):如何有效避免Cache下一 18|磁盤視角:如果內(nèi)存無限大,磁盤還有用武之地嗎精選留言展~~??老弟對于執(zhí)行內(nèi)存的(1/N/2,1/N)理解得相當?shù)轿? 3112、假如spark.executor.cores13個Task展1.1,單個Executor就是純粹的串行計算了,Spark“分布式”計算引擎2.short-lived,也就是生命周期都很短,不像StorageMemory的緩存對象、生命周期很長。因此,執(zhí)行任務涉及到的對象,多是放在年輕代,MinorGC的觸發(fā)會頻繁一些,回收效率也更高。不像long-livedobjects,往往需要觸發(fā)FullGC才能回收。因此,即便有還未回收的內(nèi)存,也不影響任務執(zhí)行,因為Minor3這個buffer緩沖每次拉取48m數(shù)據(jù)。是ExecutionMemory剩余部分不夠48m就會oom展ExecutionMemory的“賬上”。因此,如果像你說的這種edgecase,連48M都沒有了,那拉2100MB;2.調(diào)整Executor線程池、內(nèi)存、并行度等相關配置,提高1/N上限到300M展<N~<NNk~NN~Neionor1/??1老師,問一下,數(shù)據(jù)膨脹導致OOM的例子中,一定會出現(xiàn)OOM嗎?既然Task1展Spark可以保證依次運行這3個Tasks,確實不會OOM。不過,這里的1遇到過執(zhí)行查詢SQL,結果集太大,oom。通過調(diào)maxResultSize展1.task1

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論