![]()
2023年,Pinterest的工程師們還在凌晨3點(diǎn)被警報(bào)驚醒。某個(gè)推薦系統(tǒng)的Spark作業(yè)跑了4小時(shí),在收尾階段內(nèi)存溢出,一切歸零。這種戲碼每周上演數(shù)十次,團(tuán)隊(duì)疲于救火,卻找不到根治辦法。
三年后,同樣的團(tuán)隊(duì)交出了截然不同的成績(jī)單:內(nèi)存溢出(OOM)故障下降96%,夜間告警銳減,工程師終于能睡個(gè)整覺。轉(zhuǎn)變的關(guān)鍵,藏在Spark一個(gè)幾乎沒人用的參數(shù)里。
凌晨的警報(bào)與"薛定諤的內(nèi)存"
Pinterest的數(shù)據(jù)平臺(tái)支撐著每月數(shù)億用戶的個(gè)性化推薦。這些推薦背后,是數(shù)千個(gè)Spark作業(yè)在日夜運(yùn)轉(zhuǎn),處理PB級(jí)的用戶行為數(shù)據(jù)、圖像特征和實(shí)時(shí)交互日志。
但內(nèi)存管理一直是Spark的阿喀琉斯之踵。OOM錯(cuò)誤像慢性病一樣纏繞著工程團(tuán)隊(duì)——作業(yè)可能在任何階段崩潰,尤其是在Shuffle階段或聚合操作的高峰期。最折磨人的是不確定性:同樣的配置,昨天能跑完,今天就可能炸掉。
「我們像是在黑暗中射箭,」一位參與優(yōu)化的工程師回憶,「你知道內(nèi)存不夠,但不知道具體哪里不夠,更不知道什么時(shí)候會(huì)爆。」
傳統(tǒng)的應(yīng)對(duì)方式笨拙而耗時(shí):作業(yè)失敗后,工程師手動(dòng)調(diào)高executor內(nèi)存或并行度,重新提交,祈禱這次能撐過去。這種"試錯(cuò)式調(diào)參"不僅消耗人力,還造成資源浪費(fèi)——為了避免再次失敗,團(tuán)隊(duì)往往過度配置內(nèi)存,導(dǎo)致集群利用率低下。
2023年的內(nèi)部統(tǒng)計(jì)顯示,OOM相關(guān)故障占所有Spark作業(yè)失敗的23%,平均每個(gè)故障需要47分鐘的人工介入。對(duì)于需要小時(shí)級(jí)延遲的實(shí)時(shí)推薦管道,這意味著用戶體驗(yàn)的直接損失。
問題的根源比表面更深層。Spark的內(nèi)存模型將堆內(nèi)存劃分為執(zhí)行內(nèi)存和存儲(chǔ)內(nèi)存,但兩者的動(dòng)態(tài)邊界在實(shí)際運(yùn)行中難以預(yù)測(cè)。當(dāng)執(zhí)行內(nèi)存需求突增時(shí),存儲(chǔ)內(nèi)存的回收機(jī)制往往來不及響應(yīng),導(dǎo)致OOM。
從"救火"到"防火":三步改造
Pinterest的解決方案不是單點(diǎn)突破,而是一套組合拳。團(tuán)隊(duì)將其總結(jié)為三個(gè)遞進(jìn)階段:看清問題、預(yù)防崩潰、自動(dòng)恢復(fù)。
第一階段是構(gòu)建細(xì)粒度的內(nèi)存可觀測(cè)性。工程師們開發(fā)了自定義的內(nèi)存剖析工具,能夠追蹤每個(gè)executor在不同執(zhí)行階段的內(nèi)存占用曲線。這些指標(biāo)被實(shí)時(shí)采集到監(jiān)控系統(tǒng),形成"內(nèi)存熱力圖"——哪些操作是內(nèi)存黑洞,哪些階段的峰值可以被預(yù)測(cè),終于變得可見。
數(shù)據(jù)揭示了幾個(gè)反直覺的發(fā)現(xiàn)。團(tuán)隊(duì)原以為Shuffle階段是OOM重災(zāi)區(qū),但監(jiān)控顯示,超過60%的OOM發(fā)生在用戶自定義的聚合函數(shù)(UDAF)執(zhí)行期間。這些函數(shù)由業(yè)務(wù)團(tuán)隊(duì)編寫,內(nèi)存效率參差不齊,卻長(zhǎng)期缺乏審查機(jī)制。
第二階段是針對(duì)性調(diào)優(yōu)。基于觀測(cè)數(shù)據(jù),團(tuán)隊(duì)建立了一套配置模板庫(kù),按作業(yè)類型(ETL、機(jī)器學(xué)習(xí)訓(xùn)練、實(shí)時(shí)流處理)推薦初始資源配置。更關(guān)鍵的是引入了"內(nèi)存預(yù)算"概念——在作業(yè)提交時(shí)預(yù)估峰值內(nèi)存,超出預(yù)算的作業(yè)會(huì)被攔截,強(qiáng)制優(yōu)化后再上線。
但前兩個(gè)階段仍有盲區(qū):無法覆蓋所有邊緣情況,也無法消除突發(fā)流量帶來的不確定性。真正的突破來自第三階段——自動(dòng)內(nèi)存重試機(jī)制。
這個(gè)機(jī)制的核心是Spark的spark.executor.memoryOverhead參數(shù),以及一個(gè)被長(zhǎng)期忽視的動(dòng)態(tài):當(dāng)executor因OOM被殺死時(shí),YARN或Kubernetes可以自動(dòng)重新調(diào)度,但默認(rèn)情況下不會(huì)調(diào)整資源配置。
Pinterest的工程師修改了這一行為。他們?cè)谡{(diào)度層植入邏輯:檢測(cè)到OOM失敗后,自動(dòng)增加memoryOverhead(堆外內(nèi)存)并重試作業(yè),重試次數(shù)和增量幅度可配置。第一次OOM后增加10%,第二次增加20%,第三次觸發(fā)人工介入。
「這有點(diǎn)像給汽車裝自動(dòng)換擋,」項(xiàng)目負(fù)責(zé)人解釋,「上坡時(shí)動(dòng)力不足,系統(tǒng)自動(dòng)降檔,而不是讓車熄火。」
96%背后的數(shù)字游戲
自動(dòng)重試上線后的效果超出預(yù)期。2024年Q3的數(shù)據(jù)顯示,OOM故障從每周平均127起降至5起,降幅96%。更隱蔽的收益是資源效率——由于不再需要為"以防萬一"而過度配置,集群整體內(nèi)存利用率提升了18%。
但這個(gè)數(shù)字需要拆解理解。96%的降幅針對(duì)的是"因內(nèi)存不足導(dǎo)致的作業(yè)失敗",而非所有OOM事件。部分OOM由代碼缺陷(如無限循環(huán)遞歸)引起,這類問題仍需人工修復(fù)。此外,自動(dòng)重試增加了約3%的計(jì)算資源消耗(重試作業(yè)的額外開銷),但相比人工介入成本和延遲損失,這筆賬明顯劃算。
團(tuán)隊(duì)還發(fā)現(xiàn)了意外的副作用。自動(dòng)重試機(jī)制暴露了之前被掩蓋的代碼質(zhì)量問題——某些作業(yè)反復(fù)觸發(fā)重試,成為"重試釘子戶"。這些作業(yè)被標(biāo)記出來,推動(dòng)業(yè)務(wù)團(tuán)隊(duì)重構(gòu)了12個(gè)核心管道,從根本上消除了內(nèi)存泄漏。
「我們?cè)局幌胫寡Y(jié)果順便做了體檢,」工程師總結(jié),「自動(dòng)重試像是一個(gè)探針,把隱藏的技術(shù)債照了出來。」
這套方案的另一價(jià)值在于可遷移性。Pinterest將核心邏輯抽象為開源插件,適配了YARN和Kubernetes兩種部署模式。社區(qū)反饋顯示,類似的重試機(jī)制在Netflix和Uber的Spark集群中也有獨(dú)立實(shí)現(xiàn),但Pinterest的版本在策略靈活性(支持自定義重試曲線)和觀測(cè)集成上更為成熟。
行業(yè)鏡像:為什么大廠都在"修Spark"
Pinterest的案例并非孤例。過去兩年,Spark的內(nèi)存管理成為數(shù)據(jù)工程領(lǐng)域的熱門議題。Databricks推出了自適應(yīng)查詢執(zhí)行(AQE)的內(nèi)存優(yōu)化版,Apple在VLDB論文中披露了基于機(jī)器學(xué)習(xí)的Spark資源預(yù)測(cè)系統(tǒng),字節(jié)跳動(dòng)則開源了針對(duì)Shuffle的列式存儲(chǔ)優(yōu)化。
這背后的共同壓力是:數(shù)據(jù)規(guī)模的增長(zhǎng)速度超過了硬件成本的下降速度。Pinterest的推薦模型參數(shù)量三年增長(zhǎng)了17倍,但單節(jié)點(diǎn)內(nèi)存容量?jī)H翻倍。在成本約束下,"榨干現(xiàn)有資源"比"堆機(jī)器"更具商業(yè)合理性。
但優(yōu)化Spark的門檻正在抬高。早期的調(diào)優(yōu)依賴個(gè)人經(jīng)驗(yàn),現(xiàn)在則需要系統(tǒng)性的方法論。Pinterest的團(tuán)隊(duì)花了8個(gè)月才完成全套改造,其中5個(gè)月用于構(gòu)建可觀測(cè)性基礎(chǔ)設(shè)施——沒有數(shù)據(jù),連問題邊界都說不清。
「很多團(tuán)隊(duì)想抄作業(yè),直接復(fù)制我們的重試配置,」項(xiàng)目負(fù)責(zé)人透露,「但如果不先解決'看不見'的問題,自動(dòng)重試只會(huì)把系統(tǒng)拖入無限循環(huán)。」
這也解釋了為什么Spark社區(qū)至今沒有內(nèi)置類似的自動(dòng)重試機(jī)制——它太依賴具體環(huán)境的觀測(cè)數(shù)據(jù),難以做成通用方案。Pinterest的插件提供了框架,但每個(gè)公司仍需填入自己的指標(biāo)體系和策略偏好。
一個(gè)值得玩味的細(xì)節(jié)是:自動(dòng)重試機(jī)制上線后,Pinterest的Spark作業(yè)平均執(zhí)行時(shí)間反而增加了4%。這不是性能倒退,而是資源分配更貼近實(shí)際需求的結(jié)果——之前為了規(guī)避OOM而預(yù)留的"安全余量"被釋放出來,作業(yè)以更緊湊的配置運(yùn)行,整體吞吐量提升。
這有點(diǎn)像交通規(guī)劃:拓寬道路未必緩解擁堵,精確的信號(hào)燈調(diào)度反而更有效。
2025年初,Pinterest將這套方案推廣到實(shí)時(shí)流處理場(chǎng)景(Spark Structured Streaming),OOM故障率同樣下降90%以上。但流作業(yè)的優(yōu)化更具挑戰(zhàn)——延遲敏感,重試窗口以秒計(jì),容錯(cuò)機(jī)制需要與水位線管理深度耦合。
團(tuán)隊(duì)正在實(shí)驗(yàn)的下一步,是將內(nèi)存預(yù)測(cè)模型與自動(dòng)擴(kuò)縮容結(jié)合:在OOM發(fā)生前主動(dòng)調(diào)整資源,而非事后補(bǔ)救。這要求更細(xì)粒度的時(shí)間序列預(yù)測(cè),技術(shù)難度呈指數(shù)級(jí)上升。
「我們現(xiàn)在能回答'作業(yè)會(huì)不會(huì)OOM',」工程師說,「下一步要回答的是'什么時(shí)候會(huì)OOM,提前多久干預(yù)最有效'。」
當(dāng)96%的故障消失后,剩下的4%成了最難啃的骨頭——它們往往涉及復(fù)雜的業(yè)務(wù)邏輯和跨系統(tǒng)交互,無法通過配置調(diào)整解決。Pinterest的做法是將這些案例沉淀為知識(shí)庫(kù),用模式匹配輔助人工診斷。
這套系統(tǒng)的最終形態(tài),可能不是"消滅OOM",而是"OOM可控"——像現(xiàn)代航空業(yè)對(duì)待故障一樣,承認(rèn)其不可避免,但將風(fēng)險(xiǎn)壓縮到可接受的區(qū)間。
對(duì)于那些仍在凌晨處理Spark告警的團(tuán)隊(duì),Pinterest的經(jīng)驗(yàn)提供了一條可行路徑:先讓自己"看見",再讓自己"偷懶",最后讓系統(tǒng)"自愈"。代價(jià)是8個(gè)月的工程投入,和對(duì)一個(gè)被忽視參數(shù)的重新理解。
如果你的Spark集群今晚又OOM了,你會(huì)選擇手動(dòng)調(diào)參,還是開始建監(jiān)控儀表盤?
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.