知乎大數(shù)據(jù)如何降本增效?
一、背景介紹
首先對知乎做一個簡要介紹。
1. 高質(zhì)量的在線問答社區(qū)
用戶可以在知乎上進(jìn)行提問、回答、點贊和評論,獲取有特色且高質(zhì)量的內(nèi)容。
2. 混合云架構(gòu)
使用了多個云廠商的服務(wù),包括 LaaS、SaaS、PaaS 平臺等,這也提高了降本的難度。
3. FinOps
知乎內(nèi)部 2022 年開始建設(shè)一套經(jīng)濟(jì)化計費體系——FinOps,以此系統(tǒng)為抓手推動公司內(nèi)部降本實踐。FinOps 的運用強(qiáng)調(diào)了降本增效不僅是研發(fā)側(cè)的問題,更多的是與組織相關(guān)的,是需要團(tuán)隊之間共同合作才能達(dá)成的目標(biāo)。
二、FinOps 驅(qū)動降本
1. 企業(yè)內(nèi)降本的挑戰(zhàn)
(1)供應(yīng)商眾多:需要多個云廠商,會采購不同的資源,比如物理機(jī)、虛擬機(jī)等一些基礎(chǔ)資源,同時也會采購 SaaS 服務(wù)使用的資源,資源種類和供應(yīng)商眾多都為管理成本帶來了困擾。
(2)組織架構(gòu):為不斷提高組織效率,公司組織架構(gòu)會在一定周期內(nèi)進(jìn)行調(diào)整。但是在統(tǒng)計計費成本時,計費對象通常就是一些團(tuán)隊和組織,若企業(yè)組織架構(gòu)發(fā)生調(diào)整,計費對象就可能消失了。此時,我們需要一種妥善的手段去應(yīng)對組織架構(gòu)調(diào)整,保證計費體系的連續(xù)性。
(3)難以持續(xù):降本增效雖然在過去已經(jīng)有無數(shù)人進(jìn)行了實踐,但是對于運動式的降本增效,雖然在半年或三個月有了收益,但是一段時間后,組織結(jié)構(gòu)成本又逐漸提高。也就是說,整個降本增效并不是一個可持續(xù)的過程,根據(jù) Gartner 的統(tǒng)計,只有 11% 的企業(yè)能夠在三年的時間粒度上實現(xiàn)持續(xù)的成本降低。我們在執(zhí)行降本增效的過程中不僅要考慮如何通過運動式的降本增效把成本降低,還要考慮細(xì)水長流,實現(xiàn)可持續(xù)的降本。
2. FinOps
FinOps 是通過財務(wù)來驅(qū)動研發(fā),兩個部門進(jìn)行協(xié)同,共同實現(xiàn)組織降本增效的一種文化。它提出了六個原則:團(tuán)隊協(xié)作、成本節(jié)省人人有責(zé)、中心化團(tuán)隊驅(qū)動 FinOps、實時報表助力決策、業(yè)務(wù)價值驅(qū)動決策、靈活利用云上成本模型。這些原則可以濃縮成三個重要的方面:
- 第一方面需要真實且透明地度量成本,其中強(qiáng)調(diào)真實,難點在于真實刻畫業(yè)務(wù)活動中的開銷,比如我們平臺團(tuán)隊在向業(yè)務(wù)提供服務(wù)時并不是直接采購一個硬件給業(yè)務(wù)方使用,而是在上面架構(gòu)一個平臺,平臺再提供業(yè)務(wù)能力,讓業(yè)務(wù)去用而產(chǎn)生成本,真實的開銷不好計算,因為常常存在多個業(yè)務(wù)共享一個平臺。另外還要具有可比性,公司內(nèi)部提供的服務(wù)和公司外部提供的服務(wù)應(yīng)該是一樣的,我們需要具備相對的競爭力,如果我們提供一個數(shù)據(jù)庫的成本遠(yuǎn)高于在云上購買一個數(shù)據(jù)庫,那么就更應(yīng)該去云上購買這個服務(wù)而不是由組織內(nèi)部建設(shè),所以我們的成本應(yīng)該是可以和業(yè)內(nèi)的主流成本去對標(biāo),不應(yīng)該有特別大的差別。透明是指我們需要讓成本越精細(xì)越好,需要看到每一項資源的準(zhǔn)確開銷,同時團(tuán)隊間也能看到對方的成本,從而驅(qū)動業(yè)務(wù)進(jìn)行降本。以上均為成本度量的問題。
- 第二方面是中心化團(tuán)隊的支持,我們在實施降本的過程中,必須要有一個專門的全職團(tuán)隊來負(fù)責(zé)整個計費和整個 FinOps 的建設(shè),這個團(tuán)隊需要得到老板的授權(quán),通過技術(shù)管理運營的手段去驅(qū)動降本的步伐往前走。因為降本是整個公司的目標(biāo)而不是某個人某個團(tuán)隊的目標(biāo),需要專門的團(tuán)隊進(jìn)行協(xié)調(diào)溝通。這將影響到最終的降本成果。
- 第三方面是需要對降本的結(jié)果進(jìn)行階段性總結(jié),包括績效考評,比如在降本前需指定降本目標(biāo),在一個周期完成之后,回顧這個目標(biāo),誰做的好,誰做的不好,好的原因是什么,不好是出現(xiàn)了什么問題。我們將好的實踐推廣出去,不好的團(tuán)隊也需要有懲罰措施,獎懲分明,使降本動作是可持續(xù)的。
3. 成本分?jǐn)傮w系
從費用來源來看,有兩種分?jǐn)偡绞剑褐苯佑嬞M和二次分?jǐn)?。直接計費,例如業(yè)務(wù)需要存儲圖片,在云上買了一個對象存儲,這個對象存儲只有一個業(yè)務(wù)方在用,所以我們可以將這個云上存儲的賬單直接分給業(yè)務(wù),這種方式為直接計費。這種方式在公司內(nèi)部并不多,更多的時候服務(wù)是由平臺團(tuán)隊間接提供的。比如容器團(tuán)隊購買一批虛擬機(jī)或物理機(jī),在這上面去搭建 PBS 服務(wù),業(yè)務(wù)使用的其實是 PBS 上的這些容器,并沒有直接去使用這個機(jī)器。同時為了保證業(yè)務(wù)的擴(kuò)容縮容等特殊需求,平臺需要進(jìn)行池化策略,比如一些業(yè)務(wù)會共享資源池,而且這個資源池可能會有 buffer,這些冗余也會提高成本。這時就不能采用直接計費的方式,而是需要通過一些計費形式來額外地和業(yè)務(wù)方去核算。
在上圖右側(cè)圖中可以看到,最上面是最原始的一些成本來源,比如機(jī)器、SaaS 服務(wù),或者從第三方供應(yīng)商采購的軟件或硬件;中間是平臺層,包括數(shù)據(jù)團(tuán)隊、技術(shù)架構(gòu)團(tuán)隊,容器、DB 和緩存,甚至 MQ 等都是屬于 PaaS 層,PaaS 也就是平臺團(tuán)隊采購這些機(jī)器,包裝成服務(wù)提供給業(yè)務(wù)使用,這時就需要進(jìn)行成本的二次分?jǐn)?;最下層就是業(yè)務(wù)團(tuán)隊,又分為很多級,每一級有自己的分?jǐn)傔壿嫛?/span>
從計費方式上來看,分成固定單價模型和百分比分?jǐn)偰P?。固定單價模型顧名思義,以 HDFS 存儲為例,根據(jù)用戶用的 GB 或 PB,用每臺的單價乘存儲數(shù)量即為成本。百分比分?jǐn)偰P偷暮x是,比如兩個業(yè)務(wù)一起購買了一個向量數(shù)據(jù)庫服務(wù),但是因為用量較小,約定這個數(shù)據(jù)庫的成本一人一半,這樣就是各自 50% 的分?jǐn)?。在實際中需要根據(jù)資源的特點選擇合適的計費方式,在設(shè)計時可能會忽略的一個問題——激勵相容問題,指業(yè)務(wù)的降本動作和它的計費模型應(yīng)該是匹配的,計費模型應(yīng)該為業(yè)務(wù)的降本動作提供動機(jī)。以百分比分?jǐn)倿槔?,比如兩個業(yè)務(wù)約定各 50% 的分?jǐn)偙壤?,但是有業(yè)務(wù)把它的存儲和使用做了很好的優(yōu)化,但是因為計費模式還是 50%,因此團(tuán)隊的成本并沒有降低,此時我們給了業(yè)務(wù)一個負(fù)反饋,雖然降低了成本提高了效率,但在賬單上沒有任何的體現(xiàn)。如果采用固定單價模型的話就會有所體現(xiàn),比如按照存儲量來計費,當(dāng)存儲量降低的時候,這個降本的收益會體現(xiàn)在團(tuán)隊的成果上面,這時業(yè)務(wù)人員就會更有動力進(jìn)行降本的動作。這也是在做計費體系的時候需要考慮的問題。
第三個是使用按 Usage/ Capacity 付費的問題,兩種計費方式,第一種是按 Usage 計費,跟云上很相似,付費與用量成正比;第二種是按 Capacity 計費,即按照資源包付費,比如買一個 RDS 實例,用的時候即便磁盤沒有用完,但仍需要支付一個實例的費用。在我們公司,計費系統(tǒng)通常采用容量計費的方式設(shè)計。如果按使用量計費,由于每個服務(wù)會根據(jù)實際需求動態(tài)擴(kuò)縮容,每個容器的規(guī)格和數(shù)量都在不斷變化,為了準(zhǔn)確計算資源消耗,需要對每個容器的使用時間和消耗量進(jìn)行積分計算,這種按量付費的模式會增加計算負(fù)擔(dān),并且由于變化太快不利于平臺團(tuán)隊進(jìn)行容量規(guī)劃。因此,大多數(shù)情況下,我們采用容量付費模式。
在這種模式下,業(yè)務(wù)團(tuán)隊需要預(yù)先申請資源配額,比如團(tuán)隊的項目一共需要多少內(nèi)存和存儲空間。我們的計費方式是按照申請的存儲容量乘以單價進(jìn)行收費,無論資源是否被完全利用,費用都是相同的。這樣,業(yè)務(wù)可以根據(jù)實際需求調(diào)整配額,并通過設(shè)置使用上限來控制成本。由于調(diào)整配額的成本較高,業(yè)務(wù)通常會減少調(diào)整頻率,使得成本在一定時期內(nèi)保持穩(wěn)定。對于平臺方來說,這種容量計費方式有助于更好地規(guī)劃資源,可以提前采購或歸還機(jī)器,從而實現(xiàn)更高效的資源管理。
4. 運營體系
在建立了完善的計費體系后,我們應(yīng)能夠準(zhǔn)確計算每個業(yè)務(wù)和項目的實際開銷。然而,要真正實現(xiàn)降本的效果,還需要一套完善的運營體系。這套體系包括成本預(yù)警、異動歸因和定期會議三部分。
成本預(yù)警指的是當(dāng)成本出現(xiàn)較大波動,特別是在成本突然上升并超出業(yè)務(wù)預(yù)算時,系統(tǒng)應(yīng)發(fā)出警報,提醒業(yè)務(wù)團(tuán)隊及時干預(yù)并解決問題。
異動歸因是指當(dāng)業(yè)務(wù)團(tuán)隊發(fā)現(xiàn)成本變化時,最需要的是了解成本波動的原因。成本的變化可能超出預(yù)期,甚至可能是由某個特定功能引起的額外成本。在這一方面,F(xiàn)inOps 平臺能夠通過豐富的計費項目幫助業(yè)務(wù)快速分析成本變化的原因,避免手動歸因帶來的巨大人力消耗。類似的,我們在 HDFS 上也采用了一套基于歸因算法的體系。例如,某項目組在一周內(nèi)的 HDFS 存儲成本下降了 1.85%,而其中一個項目的存儲減少了 29%,對整體成本下降貢獻(xiàn)了 164%。通過對各項貢獻(xiàn)度的排序,我們可以迅速找出導(dǎo)致成本變化的原因。
定期會議也是成本運營的重要環(huán)節(jié)。通過這些會議,我們可以定期回顧每個業(yè)務(wù)團(tuán)隊的降本措施,分析其成效和變化。此外,管理層的支持至關(guān)重要,尤其是在績效考評和激勵機(jī)制上,為這些降本措施提供支持和動力。通過這樣的運營體系,我們能夠更加有效地驅(qū)動成本管理,助力業(yè)務(wù)的可持續(xù)發(fā)展。
三、技術(shù)驅(qū)動降本
在大數(shù)據(jù)領(lǐng)域,從 2022 年到 2024 年,我們采取了多項措施,逐步降低了整體成本,主要分為存儲優(yōu)化和計算優(yōu)化兩大部分。其中,有四項措施在成本收益比方面表現(xiàn)尤為突出,存儲優(yōu)化方面包括 EC(Erasure Coding),Zstd 壓縮。計算優(yōu)化方面包括 Spark 自動調(diào)參,Remote Shuffle Service。下面對這些優(yōu)化做詳細(xì)介紹。
1. EC(Erasure Coding)
EC(Erasure Coding)技術(shù)起源于舟山,最早應(yīng)用于通信行業(yè)。當(dāng)信息在傳輸過程中,信道可能會受到干擾,導(dǎo)致數(shù)據(jù)的損壞或丟失。為了確保數(shù)據(jù)的可靠傳輸,舟山碼策略問世,用于在數(shù)據(jù)傳輸時添加適量的冗余,從而提高數(shù)據(jù)的可靠性。EC 正是基于這一原理的算法,其常見實現(xiàn)方式之一是 RS(Reed-Solomon)編碼。
EC 的基本原理是將數(shù)據(jù)分成多個塊,并通過矩陣運算為每個數(shù)據(jù)塊生成相應(yīng)的校驗塊。如上圖所示,數(shù)據(jù)塊包括 d0、d1、d2、d3,經(jīng)過 EC 的計算后生成了兩個校驗塊 c0 和 c1,最終形成六個數(shù)據(jù)塊。這意味著,原來的數(shù)據(jù)冗余了 0.5 倍,但系統(tǒng)可以容忍任意一個數(shù)據(jù)塊的丟失,并可以通過矩陣運算將數(shù)據(jù)還原回來。
在大數(shù)據(jù)存儲領(lǐng)域,EC 的降本效果顯著。傳統(tǒng)的分布式存儲通常為了保證數(shù)據(jù)的可靠性,采用三副本冗余策略,也就是說,寫入 1GB 的數(shù)據(jù)時,底層會存儲 3GB 的數(shù)據(jù)量。而通過 EC 算法,僅需 1.5 倍的冗余即可提供相同的可靠性,這不僅大幅減少了存儲需求,還降低了存儲成本,特別是在大規(guī)模數(shù)據(jù)存儲環(huán)境中效果尤為突出。
然而,使用 EC(Erasure Coding)技術(shù)也存在一定的代價。首先,EC 的計算需要額外的 CPU 資源,因此對特別高頻訪問的數(shù)據(jù)(即“熱數(shù)據(jù)”)并不適合進(jìn)行 EC 轉(zhuǎn)碼。為了有效利用 EC,必須對數(shù)據(jù)進(jìn)行評估,判斷哪些數(shù)據(jù)適合進(jìn)行 EC 轉(zhuǎn)碼,哪些不適合。比如,剛寫入的數(shù)據(jù)和頻繁讀取的數(shù)據(jù)由于可能帶來額外的 CPU 消耗,通常不適合立即進(jìn)行 EC 轉(zhuǎn)碼。因此,EC 主要適用于“溫數(shù)據(jù)”和“冷數(shù)據(jù)”。
在實際應(yīng)用中,我們對 EC 的性能進(jìn)行了測試,使用了 DFSIO 讀寫工具來模擬操作。測試結(jié)果如右圖所示,藍(lán)色線代表傳統(tǒng)的 HDFS,橙色線代表使用 EC 的 HDFS。我們采用了 RS-6-3 算法,將一個數(shù)據(jù)分成九塊,其中六塊為數(shù)據(jù)塊,三塊為校驗塊,這意味著即使有兩個塊(或兩塊磁盤或兩臺機(jī)器)發(fā)生故障,數(shù)據(jù)仍能恢復(fù)。隨著客戶端讀寫并發(fā)性的增加,EC 的讀寫性能出現(xiàn)了延遲,這是因為在更高的并發(fā)度下,需要更多的機(jī)器參與讀寫操作。相比之下,傳統(tǒng) HDFS 可能只需一兩臺機(jī)器來完成讀寫,但 EC 能夠?qū)?I/O 負(fù)載均攤到更多節(jié)點上。
對于新寫入的數(shù)據(jù)或即將過期的數(shù)據(jù),不建議使用 EC,因為 EC 編碼需要消耗大量算力,而這些數(shù)據(jù)的轉(zhuǎn)碼并不劃算。另外,EC 轉(zhuǎn)碼還可能導(dǎo)致小文件問題,因塊數(shù)量的增加(如從六塊增加到九塊)而給 NameNode 增加壓力。因此,優(yōu)先選擇相對較大的文件進(jìn)行 EC 轉(zhuǎn)碼,以減少對系統(tǒng)的負(fù)擔(dān)和資源消耗。
在引入 EC 方法后,為了有效管理和執(zhí)行 EC 轉(zhuǎn)碼任務(wù),必須構(gòu)建一個專門的 EC 服務(wù)來自動化完成這項復(fù)雜的工作。由于數(shù)據(jù)量龐大且數(shù)據(jù)狀態(tài)不斷變化,僅依靠人工是無法完成的,尤其是在新數(shù)據(jù)不斷生成和舊數(shù)據(jù)逐漸過期的動態(tài)環(huán)境下。因此,全自動服務(wù)是必不可少的。
整個 EC 服務(wù)體系分為兩個主要部分:首先是元數(shù)據(jù)倉庫,它負(fù)責(zé)為整個 Hive 表建立詳細(xì)的元數(shù)據(jù)畫像。這個畫像包含了諸如 Hive 表的分區(qū)大小、文件數(shù)、平均文件大小、訪問熱度,以及分區(qū)是否已經(jīng)過 EC 處理等關(guān)鍵信息。通過這些維度的綜合評估,系統(tǒng)可以篩選出最適合進(jìn)行 EC 轉(zhuǎn)碼的分區(qū)。篩選出來的分區(qū)隨后會被交由 EC Worker 服務(wù)進(jìn)行處理。EC Worker 是一個自研的專門執(zhí)行 EC 轉(zhuǎn)碼的服務(wù)。這個服務(wù)的設(shè)計考慮了數(shù)據(jù)的可靠性,確保數(shù)據(jù)在轉(zhuǎn)碼過程中不會丟失,并具備嚴(yán)格的事務(wù)性保障。這意味著對一個目錄進(jìn)行轉(zhuǎn)碼時,要么所有數(shù)據(jù)成功轉(zhuǎn)碼,要么如果出現(xiàn)問題則整個任務(wù)回滾,以避免中間狀態(tài)或數(shù)據(jù)丟失的情況。
此外,EC Worker 服務(wù)還需要具備冪等性,即在文件已經(jīng)過一次 EC 轉(zhuǎn)碼后,系統(tǒng)需要能夠判斷并跳過已處理過的目錄,避免重復(fù)轉(zhuǎn)碼。特別是在目錄內(nèi)容因底層重寫而發(fā)生變化的情況下,系統(tǒng)必須能夠準(zhǔn)確識別這種變化,并根據(jù)當(dāng)前狀態(tài)決定是否再次執(zhí)行 EC 轉(zhuǎn)碼。
總之,EC 服務(wù)的成功運行依賴于其自動化、事務(wù)性和冪等性的特性。這些關(guān)鍵因素確保了整個 EC 過程的可靠性和高效性,使得大規(guī)模數(shù)據(jù)環(huán)境下的 EC 轉(zhuǎn)碼任務(wù)得以順利完成。
整體收益來看,EC 方法顯著優(yōu)化了存儲資源的使用效率。通過將三副本降低為 1.5 副本的方式,在確保數(shù)據(jù)可靠性的前提下,沒有因 EC 而產(chǎn)生任何數(shù)據(jù)損失。在 2023 年初,該項目已節(jié)省了 25PB 的存儲容量,并將在未來繼續(xù)產(chǎn)生效益。然而,在實踐過程中也遇到了一些挑戰(zhàn)。例如,使用的 Hadoop 3.1.x 版本存在一個 bug,具體表現(xiàn)為在 EC 塊丟失并重建時,可能會導(dǎo)致重建的塊出現(xiàn)臟數(shù)據(jù)或亂碼,而帶來數(shù)據(jù)丟失的風(fēng)險。
2. ZSTD 壓縮
ZSTD 是 Meta 開源的一種壓縮算法,相較于傳統(tǒng)的壓縮算法如 Snappy 和 LZ4,它在壓縮率和壓縮速度方面取得了很好的平衡。如上圖中的官方性能測試結(jié)果所示,ZSTD 的壓縮率比 Snappy 提升了 30%,而壓縮性能保持不變。常用的壓縮方法是 ZSTD 1.5.6 版本中的 ‘fast=3’ 的參數(shù)設(shè)置。在知乎的數(shù)據(jù)處理中,主要采用 Parquet 的存儲格式。然而,由于部分早期的表未在 Parquet 格式上進(jìn)行壓縮,而較新的表則使用了 Snappy 壓縮算法。經(jīng)過測試,將這些表從 Snappy 壓縮算法轉(zhuǎn)換為 ZSTD 壓縮后,整體存儲將減少約 30%;如果原來的 Parquet 表未進(jìn)行壓縮,整體存儲將減少 50%~60%,收益是非常可觀的。
在對表的新產(chǎn)生數(shù)據(jù)更換壓縮算法時,需要考慮 ZSTD 的兼容性問題。因為此壓縮算法較新,許多早期版本的 Hive 和 Spark 可能并不支持,因此需要將一些支持的補丁回移到 Spark 2 和 Hive 2.1 版本中,確保表可以被下游消費和讀取。
此外,對于歷史數(shù)據(jù)的壓縮算法處理,有兩種方案可以考慮。方案一是對表進(jìn)行一次 ETL 處理,通過 INSERT OVERWRITE 對表進(jìn)行重寫。然而,這種方法存在處理速度慢的問題,因為它需要將所有數(shù)據(jù)讀取出來,進(jìn)行反序列化和計算,然后再重新序列化回去。這個過程還存在一定的風(fēng)險,例如在業(yè)務(wù)演化過程中表的 schema 可能發(fā)生變化,重寫可能改變底層的數(shù)據(jù)結(jié)構(gòu),導(dǎo)致預(yù)期之外的風(fēng)險;除此之外,重新 ETL 可能會改變數(shù)據(jù)的分布,導(dǎo)致壓縮率下降。
因此,更為理想的方案是采用一種能夠繞過 SQL 對文件直接進(jìn)行處理的算法。Parquet 文件的存儲結(jié)構(gòu)本質(zhì)上是一種行列混存的格式,具體來說,對于一個大的表格,先按行進(jìn)行切分(例如每一萬行切一塊),每個塊稱為一個行組。每個行組內(nèi)部包含多個列,這些列數(shù)據(jù)按照列存的方式進(jìn)行組織和存儲。因此,一個 Parquet 文件可能包含多個 row group,每個 row group 是按行組織的數(shù)據(jù)組,而每列數(shù)據(jù)在具體存儲時是通過 page 的方式進(jìn)行壓縮。壓縮操作發(fā)生在 page 層次上,而非對整個 Parquet 文件進(jìn)行壓縮。我們希望將舊的 page 讀取出來,用老壓縮算法解壓縮后,再用新的壓縮算法重新壓縮,這在官方的 Parquet 實現(xiàn)中是可行的,并且官方提供了名為 parquet-tools 的工具,其中定義了一部分對底層 page 進(jìn)行操作的 API,我們基于這些 API 開發(fā)了 ZSTD 轉(zhuǎn)化工具,該工具能夠非常高效地對底層文件進(jìn)行重寫,而不涉及到 schema 的變更,也無需對數(shù)據(jù)進(jìn)行任何反序列化,只是單純地將數(shù)據(jù)取出并重新壓縮。
這種方式顯著提高了處理效率,能夠在較短時間內(nèi)完成對歷史數(shù)據(jù)的處理,同時還可以在轉(zhuǎn)碼過程中對一些小文件進(jìn)行合并。根據(jù)整體估算,ZSTD 每分鐘可以處理 30GB 的數(shù)據(jù),在一個月中節(jié)省了 10PB 的存儲,新的作業(yè)已默認(rèn)采用 ZSTD 壓縮算法。同時,ZSTD 和 EC 是可以并存的,它們在不同層次上進(jìn)行存儲,收益是可以乘算的。
3. Spark 自動調(diào)參
Spark 自動調(diào)參是許多公司面臨的一個難題,主要難點在于如何合理定義 Spark 作業(yè)所需的資源。對于業(yè)務(wù)數(shù)據(jù)人員,自動調(diào)參可以幫助他們將大數(shù)據(jù)調(diào)參過程自動化。該系統(tǒng)基于作業(yè)的歷史執(zhí)行數(shù)據(jù),包括真實的內(nèi)存和 CPU 消耗,來計算出最適合作業(yè)的參數(shù)配置。
整個自動調(diào)參系統(tǒng)分為兩部分,作業(yè)畫像系統(tǒng)和自動調(diào)參服務(wù)。作業(yè)畫像系統(tǒng)負(fù)責(zé)收集并保存每個作業(yè)運行的各項指標(biāo)數(shù)據(jù);自動調(diào)參服務(wù)基于這些歷史指標(biāo)數(shù)據(jù)和預(yù)先設(shè)定的調(diào)參規(guī)則,來決定每個作業(yè)的提交參數(shù)。
作業(yè)畫像系統(tǒng)的關(guān)鍵指標(biāo)采集包括 CPU 和內(nèi)存使用率、GC 耗時、Shuffle 數(shù)據(jù)量、HDFS 讀寫統(tǒng)計值。系統(tǒng)采用了 jvm-profile 和 sparklens 開源組件,分別從 JVM 和 Spark 端采集數(shù)據(jù),這些數(shù)據(jù)匯總后為自動調(diào)參提供基礎(chǔ)支持。在提交 Spark 作業(yè)時,通常會過多申請內(nèi)存資源,實際使用的峰值可能低于預(yù)期。通過這種自動化的采集與分析,系統(tǒng)能根據(jù)歷史數(shù)據(jù)自動調(diào)整內(nèi)存分配,避免資源浪費,提高資源利用率。
作業(yè)調(diào)參系統(tǒng)通過對 Spark 作業(yè)的 spark-launcher 進(jìn)行劫持,在提交作業(yè)前先請求調(diào)參服務(wù)。調(diào)參服務(wù)會根據(jù)作業(yè)歷史的執(zhí)行數(shù)據(jù),推測最佳的 CPU 與內(nèi)存比以及執(zhí)行器數(shù)量,并將優(yōu)化后的參數(shù)返回給長字符。整個過程采用啟發(fā)式算法,逐步逼近最佳內(nèi)存配置。例如,作業(yè)最初申請了 30GB 內(nèi)存,但實際峰值僅為 16GB,系統(tǒng)會先將內(nèi)存下調(diào)至 24GB,觀察執(zhí)行耗時和 GC 情況,如果沒有顯著變化,則繼續(xù)調(diào)整,直到找到最優(yōu)參數(shù)。這個漸進(jìn)式的調(diào)優(yōu)過程能夠逐步優(yōu)化資源配置,提升作業(yè)效率。
在公司實踐中,經(jīng)過 3 個月的優(yōu)化,整體作業(yè)資源節(jié)省了約 30%。上圖中右側(cè)圖中紅色和藍(lán)色曲線分別顯示了 CPU 和內(nèi)存的優(yōu)化量,以核時和 GB 時為統(tǒng)計單位。這種調(diào)參服務(wù)現(xiàn)已覆蓋所有 Spark 作業(yè),顯著提升了資源利用效率。
4. Remote Shuffle Service
在 Spark 中,Shuffle 是數(shù)據(jù)處理的重要環(huán)節(jié)。默認(rèn)情況下,Spark 提供了三種 Shuffle 方式,其中的 Start Shuffle 在實際使用中較少被采用,因為一旦出現(xiàn)故障重啟,Shuffle 數(shù)據(jù)會丟失,必須重新計算。一般情況下,External Shuffle Service (ESS)是更常用的方式。
ESS 的工作原理是:在 NodeManager 上啟動一個 Shuffle 服務(wù),Spark 的 Executor 將 Shuffle 數(shù)據(jù)寫入本地磁盤,然后下一個計算任務(wù)的 Reducer 通過 NodeManager 讀取這些數(shù)據(jù)。
ESS 的優(yōu)勢在于,即使 Executor 關(guān)閉,數(shù)據(jù)仍然保存在磁盤上,不需要重新計算,只需恢復(fù)數(shù)據(jù),極大地提升了穩(wěn)定性。
然而,ESS 也存在一些問題。比如 Spark Shuffle 可能涉及大量分區(qū),每個分區(qū)在 Shuffle 時都會生成一個文件。HDFS 磁盤在使用時,IOPS(磁盤每秒讀取/寫入操作數(shù))較低,讀寫性能不佳。而 ESS 會導(dǎo)致大量數(shù)據(jù)的讀寫,大量的磁盤 IO 資源浪費在 Shuffle 過程的等待時間上,而不是用于真正的 CPU 密集型計算。
如圖所示,這個任務(wù)的 Shuffle Read 數(shù)據(jù)量為 9.5MB,但生成了 1.8 萬個文件,總共產(chǎn)出 11 分鐘,這是典型的磁盤處理性能。但其實應(yīng)該只需要幾秒鐘,這就是 RSS 引入的背景。
RSS(Remote Shuffle Service)實質(zhì)上為 push based shuffle,和 ESS 不同,它是由 executor 主動將數(shù)據(jù)推送到下一個 Reducer 所需的存儲位置,也就是說數(shù)據(jù)寫入的時候會按照讀的要求做一定的合并和整理。
在眾多的開源 RSS 中我們選擇了阿里的 Apache Celeborn,經(jīng)過一系列的測試,Celeborn 的穩(wěn)定性和性能都是比較好的。Celeborn 的一個重要收益是將 shuffle 過程中大量的隨機(jī)讀寫操作轉(zhuǎn)化為順序讀寫操作。寫入的時候會按照需求對 partition 做合并,數(shù)據(jù)已經(jīng)被按照順序整理好,可以進(jìn)行直接讀取。這樣減少了隨機(jī)讀寫操作,提高了磁盤吞吐量。此外,Celeborn 還支持 PartitionSplit 的功能,當(dāng)發(fā)現(xiàn) partition 太大的時候可以進(jìn)行主動的分裂,避免一個 worker 處理過多的數(shù)據(jù)。Partition 分裂這一功能在平滑升級中非常有用,例如當(dāng)某個節(jié)點需要維護(hù)或下線時,Celeborn 可以將該節(jié)點正在處理的 Partition 分裂并轉(zhuǎn)移到其他 worker,從而避免新數(shù)據(jù)經(jīng)過該節(jié)點。這使得在不中斷業(yè)務(wù)的情況下,可以平滑地處理節(jié)點下線或進(jìn)行滾動升級,確保集群的穩(wěn)定運行,對業(yè)務(wù)不產(chǎn)生任何負(fù)面影響。
統(tǒng)計結(jié)果表明,Spark 作業(yè)的 shuffle read 耗時經(jīng)過逐步將作業(yè)接入到 Celeborn worker 之后有了明顯的下降,具體來說,整體的 shuffle read P99(即 99% 作業(yè)的最大耗時)平均損耗降低了 30%。
四、總結(jié)與展望
整體來看,我們主要通過兩大手段保證了降本增效的效果,即 FinOps 系統(tǒng)建設(shè)和運營,以及技術(shù)優(yōu)化,實現(xiàn)了可持續(xù)的降本。
未來規(guī)劃,首先會推動 Hive 引擎逐步向 Spark 引擎遷移;其次利用主流方法,如 gluten+velox 進(jìn)一步提升計算效率;同時探索通過彈性EMR 和固定資源池混合的方式提高資源利用效率。