數(shù)據(jù)庫頂會 VLDB 2024 論文解讀:字節(jié)跳動如何對大規(guī)模 Spark 作業(yè)進(jìn)行資源提效
論文鏈接:https://www.vldb.org/pvldb/vol17/p3759-shi.pdf
引言
近年來 Spark 已經(jīng)成為離線大數(shù)據(jù)處理引擎的事實(shí)標(biāo)準(zhǔn),廣泛用于數(shù)據(jù)倉庫、數(shù)據(jù)湖、機(jī)器學(xué)習(xí)等領(lǐng)域。在字節(jié)跳動內(nèi)部每天運(yùn)行百萬級別的 Spark 離線作業(yè),Shuffle 量高達(dá) 500PB,CPU 資源需求達(dá)到千萬級別。隨著業(yè)務(wù)的快速發(fā)展,用戶對計(jì)算資源的需求越來越大,除了增加物理資源之外,如何提高線上 Spark 作業(yè)的資源使用效率也是我們亟需解決的問題。
在對線上 Spark 作業(yè)做了統(tǒng)計(jì)分析發(fā)現(xiàn)作業(yè)的 CPU & Memory 利用率都低于 50%(利用率指作業(yè)實(shí)際使用的資源占實(shí)際申請資源的比例);作業(yè)的 Data Scan Time 加上 Shuffle Read Block Time 占據(jù)了整個(gè)運(yùn)行時(shí)間的 45% 左右。從上述指標(biāo)可以看出,線上 Spark 作業(yè)有非常大的資源優(yōu)化空間,資源使用效率不高的原因主要有以下 3 個(gè)方面:
- Slow IO
Slow HDFS IO:離線數(shù)據(jù)存儲在 HDFS 集群,經(jīng)常會出現(xiàn)作業(yè)讀取 HDFS 慢,導(dǎo)致 CPU/Memory 等待 IO 而處于空閑狀態(tài)。
Slow Shuffle IO:線上部署了 External Shuffle Service(ESS),Shuffle 量非常大(每天超 500PB,一些作業(yè)達(dá)幾百 TB), ESS 的穩(wěn)定性是一個(gè)比較大的挑戰(zhàn),經(jīng)常出現(xiàn)很高的 Shuffle Read Block Time 導(dǎo)致 CPU/Memroy 空閑,而且會有大量的 FetchFailure 導(dǎo)致 Stage 頻繁 Retry 重算,也浪費(fèi)了大量資源。
- 粗粒度的資源控制
資源申請:Spark 作業(yè)在運(yùn)行過程中不同的 Stage 對資源的需求不一樣,雖然 Spark 通過 Dynamic Resource Allocation (DRA) 提供了橫向伸縮的能力,但是在縱向資源伸縮方面 Spark 提供的 ResourceProfile 方案并不成熟字節(jié)并未采取,導(dǎo)致大量作業(yè)運(yùn)行不同的 Stage 的時(shí)候產(chǎn)生資源浪費(fèi)的情況。
資源使用:Spark 引擎在一些算子的 Spill 策略上并不能很好的控制內(nèi)存的使用,在一些場景下還會導(dǎo)致 OOM 問題,通過完善 Spill 策略合理利用磁盤可以降低運(yùn)行中內(nèi)存的使用,從而節(jié)省內(nèi)存資源。
- 不合理的作業(yè)參數(shù)
字節(jié)內(nèi)部 Spark 用戶很多,每天運(yùn)行的作業(yè)可以達(dá)到 180w+,通過手工調(diào)參的方式,一方面浪費(fèi)測試資源以及人力成本,另一方面需要對 Spark 機(jī)制比較熟悉才能有比較好的效果,所以目前線上整體上存在很多不合理的作業(yè)參數(shù),導(dǎo)致資源利用率低,或者 Shuffle 不穩(wěn)定(AQE 相關(guān)參數(shù)不合理)。
針對以上問題字節(jié)跳動基礎(chǔ)架構(gòu)-基礎(chǔ)技術(shù)-批式計(jì)算和應(yīng)用研究中心團(tuán)隊(duì)與上海交通大學(xué)的數(shù)據(jù)通信與數(shù)據(jù)工程實(shí)驗(yàn)室合作,基于線上的實(shí)際情況從三個(gè)方面進(jìn)行了系統(tǒng)性的優(yōu)化,包括多機(jī)制的 Shuffle 優(yōu)化(穩(wěn)定資源 External Shuffle Service 增強(qiáng)、混部資源自研 Remote Shuffle Service)、細(xì)粒度的資源申請和運(yùn)行時(shí)資源使用控制、規(guī)則+算法兩個(gè)階段的自動參數(shù)調(diào)優(yōu)。經(jīng)此優(yōu)化后可以實(shí)現(xiàn)大規(guī)模上量 50萬+ 作業(yè),日均節(jié)省百萬級 CPU 核、PB 級內(nèi)存。
系統(tǒng)概覽
我們提出了一套資源提效的治理框架,如下圖所示在該框架中,貫穿整個(gè) Spark 作業(yè)運(yùn)行周期的優(yōu)化包含如下三個(gè)方面,分別對應(yīng)解決引言中的三個(gè)問題。
- Multi-Mechanism Shuffle Service
字節(jié)內(nèi)部使用開源 ESS 承載作業(yè) Shuffle,ESS 的機(jī)制需部署在 Spark 作業(yè)運(yùn)行的計(jì)算資源節(jié)點(diǎn)上,在大規(guī)模以及復(fù)雜的計(jì)算資源類型的背景下,Shuffle 穩(wěn)定性一直是個(gè)挑戰(zhàn)。針對不同的計(jì)算資源類型,我們提出了不同的 Shuffle 優(yōu)化方案,大大提升了 Shuffle 穩(wěn)定性,減少了 Shuffle IO 等待導(dǎo)致的資源空閑以及 Stage 重算導(dǎo)致的資源浪費(fèi)。
- Fine-Grained Resource Control
在 Spark 引擎層面提供了細(xì)粒度的資源申請和使用控制的優(yōu)化,增加了相關(guān)配置參數(shù),結(jié)合后續(xù)的自動調(diào)參,大大提升了資源利用率。
- Two-Stage Configuration Auto Tuning
我們針對周期性調(diào)度作業(yè)設(shè)計(jì)了基于規(guī)則和基于算法的兩階段自動調(diào)參系統(tǒng),大大降低了人工調(diào)參成本,覆蓋了線上大規(guī)模的作業(yè),可以讓作業(yè)快速收斂到合理的參數(shù)運(yùn)行。
多機(jī)制 Shuffle 服務(wù)
在 K8s 統(tǒng)一資源池的背景下,字節(jié)內(nèi)部逐漸下線 YARN,將 Spark 作業(yè)遷移到 K8s,如系統(tǒng)概覽中的圖所示,Spark 在 K8s 上的計(jì)算資源大體可以分為兩大類:
- Dedicated Cluster
- 獨(dú)占計(jì)算資源會掛載 SSD 作為 Shuffle 數(shù)據(jù)盤,磁盤能力(吞吐/ IOPS)比較強(qiáng),提供給高優(yōu)作業(yè)使用。
- Mixed Cluster
低優(yōu) Spark 作業(yè)也使用了很多混部資源,包括在離線混部/ HDFS 混部等,這些計(jì)算資源的磁盤能力相對比較差。
開源的 ESS 部署在上述兩種類型的集群中,穩(wěn)定性都遇到了非常大的挑戰(zhàn),特別是混部集群,為此我們一種多機(jī)制的 Shuffle Service 優(yōu)化,針對不同類型的集群提供對應(yīng)的解決方案。
Enhanced External Shuffle Service (ESS)
雖然 SSD 獨(dú)占集群磁盤能力強(qiáng),但是在大規(guī)模作業(yè)以及 Shuffle 量的背景下,ESS 穩(wěn)定性依然受到很多挑戰(zhàn),為此我們進(jìn)行了一系列優(yōu)化。
Request Throttling
ESS 服務(wù)是被集群上運(yùn)行的所有 Spark 共享,它在集群的每個(gè)節(jié)點(diǎn)上都部署了 Shuffle Service 服務(wù)進(jìn)程,該服務(wù)進(jìn)程面向該節(jié)點(diǎn)上運(yùn)行的所有 Spark 作業(yè)提供 Shuffle Read 服務(wù),所以個(gè)別異常作業(yè)就會影響整個(gè)集群的 Shuffle 穩(wěn)定性。線上一般有兩種異常情況的作業(yè)對 ESS 服務(wù)節(jié)點(diǎn)產(chǎn)生比較大的壓力,主要有以下原因影響穩(wěn)定性:
- Shuffle 規(guī)模本身很大的作業(yè),會產(chǎn)生大量的 Chunk Fetch 請求;
- Shuffle 規(guī)模不大,但是相關(guān)參數(shù)不合理造成 Shuffle Read 階段有大量的小于 20KB 的 Chunk Fetch 請求。
異常作業(yè)限流基于這些場景,避免異常作業(yè)影響整個(gè)集群的 Shuffle 穩(wěn)定性。主要為以下功能:
- 每個(gè) ESS 節(jié)點(diǎn)上會定期監(jiān)控請求延遲,當(dāng)延遲超過一定閾值后,該節(jié)點(diǎn)會開啟 ESS 限流;
- 開啟限流的節(jié)點(diǎn)將會定期監(jiān)控節(jié)點(diǎn)上所有注冊的 Shuffle 應(yīng)用的 Fetch 請求量,當(dāng)任何應(yīng)用的請求量超過所分配的流量時(shí),該應(yīng)用將會受到限流;
- ESS 節(jié)點(diǎn)上的應(yīng)用受到限流后,所允許堆積的請求量會有限制,如果該應(yīng)用堆積的請求量超過閾值,則暫時(shí)不能發(fā)送新的 Fetch 請求。
Executor Rolling
我們從歷史統(tǒng)計(jì)數(shù)據(jù)中觀察到,Shuffle 讀取速度慢與節(jié)點(diǎn)上寫入的 Shuffle 數(shù)據(jù)量之間存在高度相關(guān)性。其中, Shuffle 寫入量排名前五位的節(jié)點(diǎn),這貢獻(xiàn)了半數(shù)的 Shuffle 慢讀取數(shù)量,而排名前兩位的節(jié)點(diǎn)則占據(jù)了 35% 的慢讀取。為了防止 Shuffle 數(shù)據(jù)集中在某些節(jié)點(diǎn)上,避免慢 Shuffle 讀取,研發(fā) Executor 滾動(Executor Rolling)策略,將 Shuffle 寫入的數(shù)據(jù)更均勻地分布在集群中的節(jié)點(diǎn)上。主要功能如下:
- 在 Shuffle 過程中,記錄每個(gè) Executor 的 Shuffle 寫入大??;
- 當(dāng)某個(gè) Executor 寫入大小超過預(yù)定的閾值時(shí),釋放該 Executor 并請求新的 Executor;
- 同時(shí)調(diào)度器的調(diào)度策略也有助于更均勻地分配分配的容器。
Executor rolling 流程
Cloud Shuffle Service (CSS)
對于混合集群,開發(fā)了一種基于推送的遠(yuǎn)程 Shuffle 服務(wù)——CSS,它允許計(jì)算和存儲解耦,從而消除混部集群中對本地磁盤的依賴,并提高了混合集群中 Shuffle 的可靠性。CSS 整體架構(gòu)如下:
CSS 架構(gòu)概覽
- Cluster Manager 負(fù)責(zé)集群的資源分配,并維護(hù)集群 Worker 和 Application 狀態(tài),它可以通過 Zookeeper 或者本地磁盤保存這些信息,達(dá)到具有 High Availability 的服務(wù)。
- CSS Workers 支持兩種寫入模式,分別是磁盤模式和 HDFS 模式。目前常用的是磁盤模式,每個(gè)分區(qū)的數(shù)據(jù)會寫入兩個(gè)不同的 Worker 節(jié)點(diǎn),以實(shí)現(xiàn)數(shù)據(jù)冗余。
- CSS Manager Client 位于 Spark driver 端 ,主要負(fù)責(zé)與 Cluster Manager 的心跳聯(lián)系以及 Application Lifecycle。作業(yè)啟動時(shí),也會向 Cluster Manager 申請 Worker。Shuffle Stage 的過程也會統(tǒng)計(jì) Shuffle Stage 的元數(shù)據(jù)以及 Shuffle 的進(jìn)展。
- CSS Worker Client 是一個(gè)接入了 Spark Shuffle API 的組件,允許任何 Spark 作業(yè)可以直接使用 CSS 而無需額外配置。每個(gè) Executor 會使用 ShuffleClient 進(jìn)行讀寫。Shuffle Client 負(fù)責(zé)寫入時(shí)候的雙寫,在讀的時(shí)候,它可以向任何一個(gè)存有數(shù)據(jù)的 Worker 去讀取這些數(shù)據(jù),如果其中一個(gè) Worker 讀取失敗的話,也會自動切換到另一個(gè) Worker 上,并對多讀的數(shù)據(jù)進(jìn)行去重。
CSS 具有以下關(guān)鍵特性來提升性能和穩(wěn)定性:
- Push Based Shuffle 模式將相同分區(qū)的數(shù)據(jù)推送到同一組工作節(jié)點(diǎn),這些數(shù)據(jù)在刷新到磁盤前會與所有緩存在內(nèi)存中的數(shù)據(jù)合并寫入,從而允許 Shuffle Read 階段對每個(gè)分區(qū)數(shù)據(jù)進(jìn)行順序讀??;
- Partition Groups 功能允許將多個(gè)分區(qū)的 Shuffle 數(shù)據(jù)批量推送,從而減少 I/O 請求;
- 快速內(nèi)存?zhèn)浞?/strong>功能,通過內(nèi)存雙寫與異步刷盤,以較低成本提高容錯(cuò)力,避免 Stage 失敗帶來的重試成本;
- 負(fù)載均衡功能,Cluster Manager 通過周期性收集的集群指標(biāo)對 Worker 節(jié)點(diǎn)資源進(jìn)行分配。
CSS 開源地址:https://github.com/bytedance/CloudShuffleService
細(xì)粒度資源控制
Spark 對于資源的分配(CPU、MEM)不夠細(xì)致,使得資源利用率不夠高,且容易出現(xiàn) OOM。為此我們采取了一些措施提供細(xì)粒度的資源控制:
- 增加 Millicores 和 MemoryBurst 參數(shù)在配置階段更細(xì)粒度的申請資源;
- 優(yōu)化支持 Spill 的算子和 Spill 模式,減少內(nèi)存無序爭搶,利用集群的 Disk 資源。
Resource Allocation Control
字節(jié)內(nèi)部的 Spark 作業(yè),使用自研的 Yarn On Kubernetes 框架,綜合考慮了云原生的趨勢和穩(wěn)定性的要求。該框架的資源調(diào)度協(xié)議保留了 Yarn 的協(xié)議,而在資源調(diào)度底層使用的 Kubernetes。為了提升 Spark 作業(yè)的資源利用率修改了Spark on Yarn 的參數(shù)與交互協(xié)議,從而支持細(xì)粒度的 CPU 和 Mem 配置。同時(shí), 通過算子 Spill 增強(qiáng) Spark 已有的內(nèi)存管理模式。
CPU 資源控制
我們主要是通過在 Executor 內(nèi)部 Task 運(yùn)行并發(fā)度不變但是降低實(shí)際的 CPU 申請值的方式來提升 CPU 利用率。在開源社區(qū)版本的 Spark 中,spark.executor.cores 有兩個(gè)含義,既代表 Executor 向資源調(diào)度系統(tǒng)(YARN,Kubernetes)申請的 Cores 數(shù),同時(shí)也代表在 Executor 內(nèi)部任務(wù)運(yùn)行的并發(fā)度,一個(gè) Task 運(yùn)行至少需要 1 Core。我們在 Spark 中引入了一個(gè)新的參數(shù)——spark.executor.millicores,該參數(shù)被設(shè)置時(shí)實(shí)際創(chuàng)建的 Executor 的 CPU Request 會使用該參數(shù)的值,spark.executor.cores 只代表 Executor 中 Task 的并發(fā)度。為了保證作業(yè)的運(yùn)行速度,在降低 CPU Request 的同時(shí)需要設(shè)置一個(gè)較大的 CPU Limit,但為了避免過高的超發(fā),我們在實(shí)踐中沒有暴露參數(shù)給用戶直接設(shè)置 CPU Limit,而是在不同的集群設(shè)置了各自的超發(fā)系數(shù),默認(rèn)情況下 Limit 會設(shè)置成 Request 的兩倍。
Memory 資源控制
Spark Executor 使用的內(nèi)存可以大致分成兩部分:堆內(nèi)內(nèi)存和 Overhead,在默認(rèn)的 onHeap 模式下,大部分的運(yùn)行時(shí)使用的內(nèi)存都在堆內(nèi),且這部分內(nèi)存被 JVM 所管理,缺少彈性。調(diào)整堆內(nèi)內(nèi)存的風(fēng)險(xiǎn)很高,容易引發(fā) OOM 異常。而 Overhead 的使用更靈活,調(diào)整 Overhead 的風(fēng)險(xiǎn)更低。我們新增了 spark.executor.memoryBurst.ratio 參數(shù),允許 Spark 申請 Executor 時(shí),按照該參數(shù)設(shè)置的比例降低 Overhead 內(nèi)存的 Request 申請,Limit 保持不變。
資源控制示例
Resource Usage Control
Spark 對作業(yè)的內(nèi)存管理比較粗糙,容器運(yùn)行的時(shí)候多個(gè)算子盡可能將內(nèi)存用滿,只有當(dāng)內(nèi)存不足的時(shí)候才會觸發(fā) spill 操作,數(shù)據(jù)溢寫到磁盤。這種無序的內(nèi)存爭搶是作業(yè) OOM 的主要原因。而線上集群磁盤利用率并不高,完全有足夠的空間支持把更多的數(shù)據(jù)溢寫到磁盤。
為此,我們對 Spark 已有的內(nèi)存管理模式做了改進(jìn),覆蓋更多算子的 Spill,包括 UnsafeExternalSorter、HashAggregateExec。同樣的,我們在 Spark 原有的 Force Spill by Number of Records 模式上,增加了多種算子級別的 Spill 模式:Force Spill by Memory used, Allow Spill by Memory used, Allow Spill by Fraction of memory used。
通過 Spill 機(jī)制的改進(jìn),我們可以精確控制 Spark Operators 使用的內(nèi)存,從而確保內(nèi)存的分配和釋放更加高效。
兩階段自動調(diào)參
Spark 作業(yè)的配置參數(shù)對資源利用率有著顯著影響。然而在生產(chǎn)環(huán)境中,對每個(gè)作業(yè)進(jìn)行參數(shù)調(diào)優(yōu)實(shí)驗(yàn)是不切實(shí)際的。因此我們專門為周期性作業(yè)建立了一套 Online Tuning Pipeline,充分利用運(yùn)行時(shí)的指標(biāo)數(shù)據(jù)。與預(yù)先調(diào)優(yōu)參數(shù)不同,我們從默認(rèn)或用戶定義的參數(shù)(通常是次優(yōu)的)開始,并記錄每個(gè)作業(yè)的運(yùn)行指標(biāo)。隨后對這些指標(biāo)進(jìn)行分析,以改進(jìn)下一次作業(yè)執(zhí)行的參數(shù)。為了實(shí)現(xiàn)在線調(diào)優(yōu)的快速穩(wěn)定收斂,我們開發(fā)了一種如下圖所示的兩階段配置自動調(diào)優(yōu)方法。第一階段是基于規(guī)則的調(diào)優(yōu),利用由 Spark 專家手動編寫的規(guī)則,從而避免了低效的探索。第二階段是基于算法的調(diào)優(yōu),改進(jìn)了貝葉斯優(yōu)化算法以提高穩(wěn)定性,旨在找到更優(yōu)的參數(shù),同時(shí)將生產(chǎn)環(huán)境中發(fā)生 OOM(內(nèi)存不足)故障的概率降至最低。
Online Tuning Pipeline
上圖展示了 Spark Tuning Framework 的工作流程與該框架包含的四個(gè)組件:
- Tunning API 控制器負(fù)責(zé)與數(shù)據(jù)平臺和最終用戶進(jìn)行交互,記錄每個(gè)任務(wù)的優(yōu)化配置,供用戶查詢?nèi)蝿?wù)監(jiān)控?cái)?shù)據(jù)。
- JobAnalyzer 是一個(gè) FlinkJob 消費(fèi) Spark 運(yùn)行過程中的 Event log 數(shù)據(jù)以及調(diào)度系統(tǒng)的其他數(shù)據(jù)來實(shí)時(shí)生成任務(wù)的運(yùn)行指標(biāo)。
- Rule-Based Tuning:由若干啟發(fā)式算法構(gòu)成,輸入作業(yè)的運(yùn)行指標(biāo),該規(guī)則樹會按照啟發(fā)式規(guī)則在樹中的關(guān)系最終生成推薦參數(shù)。
- Algorithm-Based Tuning:針對在線調(diào)參的安全性要求進(jìn)行特別優(yōu)化的貝葉斯優(yōu)化算法,該算法根據(jù)歷史參數(shù)的運(yùn)行情況生成性能更優(yōu)的推薦參數(shù)。
Rule-Based Tuning
每個(gè)任務(wù)通過聚合生成上一節(jié)所展示的指標(biāo)后,我們就得到了一個(gè)任務(wù)大致的畫像,例如這個(gè)任務(wù)有多少個(gè) Task,輸入了多少數(shù)據(jù),平均和最大的 CPU 利用率如何等。我們依賴這些指標(biāo)嘗試使用啟發(fā)式算法對作業(yè)的參數(shù)進(jìn)行調(diào)優(yōu),在實(shí)踐中由于針對啟發(fā)式算法越來越多,我們使用了一個(gè)規(guī)則樹來描述規(guī)則與規(guī)則之間的關(guān)系。
這些規(guī)則調(diào)整的參數(shù)可以分為三類:對于 CPU 和 內(nèi)存來說,啟發(fā)式規(guī)則最基本的方式可以描述為當(dāng)平均利用率和最大利用率較低時(shí),就在任務(wù)并發(fā)度不變的前提下降低對應(yīng)的資源申請量,當(dāng)利用率過高時(shí),就增大對應(yīng)資源的申請量;Shuffle 優(yōu)化如上文所講,導(dǎo)致 Shuffle 問題的主要原因是 ESS 存在大量的隨機(jī) IO,使用更優(yōu)的參數(shù)可以有效的減少隨機(jī) IO 次數(shù)。啟發(fā)式規(guī)則會觀察作業(yè)每個(gè) Stage 的 Partition 數(shù)量,當(dāng) Partition 數(shù)量遠(yuǎn)大于任務(wù)能申請到的 Core 的數(shù)量時(shí),會被認(rèn)為該并發(fā)度是不必要的。
Algorithm-Based Tuning
為了應(yīng)對那些無法通過啟發(fā)式規(guī)則調(diào)優(yōu)有效優(yōu)化的作業(yè),我們開發(fā)了一種基于算法的調(diào)優(yōu)方法,采用了貝葉斯優(yōu)化算法。目標(biāo)是找到能最小化參數(shù)評估函數(shù)的配置。為了提升利用率,評估指標(biāo) f(x) 定義成為 CPU 和內(nèi)存利用率乘積的倒數(shù)。我們選擇高斯過程 (GP) 作為目標(biāo)函數(shù)的替代模型。
在這個(gè)模型中,Expected Improvement(EI) 通常被用作為采集函數(shù)。它衡量了未見參數(shù) x, 可能帶來的期望改進(jìn)。作業(yè)下一次運(yùn)行的推薦參數(shù),x* 可以通過最小化采集函數(shù)獲得。為了求解這個(gè)優(yōu)化問題,我們使用遺傳算法,以便對參數(shù)空間進(jìn)行高效且有針對性的探索。
算法調(diào)優(yōu)能夠更高效且有針對性地探索參數(shù)空間,在提升 Spark 作業(yè)資源利用率的同時(shí)確保穩(wěn)定性。符號說明如下:
實(shí)驗(yàn)及成果
這部分內(nèi)容將從穩(wěn)定性、性能和資源利用率的角度分析并回答以下問題:
- Shuffle 服務(wù)提升了多少性能?
- 資源控制如何幫助提高穩(wěn)定性和利用率?
- 通過配置調(diào)優(yōu)可以節(jié)省多少資源?
- 這些技術(shù)在生產(chǎn)環(huán)境中的優(yōu)勢是什么?
Enhanced ESS and CSS Evaluations
我們評估了多機(jī)制 Shuffle 服務(wù)帶來的效果,包括通過生產(chǎn)工作負(fù)載評估增強(qiáng)型 ESS 的穩(wěn)定性,以及使用 TPC-DS Benchmark 評估 CSS 的性能。
Enhanced ESS
Request Throttling 功能通過其在生產(chǎn)集群中的表現(xiàn)進(jìn)行評估。下圖展示了請求節(jié)流對在一段時(shí)間內(nèi)向 ESS 節(jié)點(diǎn)發(fā)送大量 Shuffle 請求的作業(yè)的影響。當(dāng)由于 Shuffle 請求數(shù)量增加導(dǎo)致 ESS 服務(wù)器的 Shuffle 延遲開始惡化時(shí),請求節(jié)流對貢獻(xiàn)最多 Shuffle 請求的作業(yè)生效,減少該作業(yè)發(fā)送的后續(xù) Shuffle 請求數(shù)量。在幾分鐘內(nèi),ESS 能夠完成其排隊(duì)的請求處理,Shuffle 延遲很快恢復(fù)正常。
Executor Rolling 功能也在生產(chǎn)集群上進(jìn)行了評估,比較了啟動執(zhí)行器滾動前后的磁盤使用情況。選擇執(zhí)行器滾動啟動前的 2023 年 1 月的一天和啟動后的 2024 年 1 月的一天的磁盤使用數(shù)據(jù),如下圖所示隨著業(yè)務(wù)的增長,每臺物理機(jī)上 Shuffle 的中位數(shù)磁盤使用量從 0.7TB 增加到 1.2TB,平均值從 1.8TB 增加到 2.6TB,而最大值從 48TB 降低到 23TB。第 99 百分位數(shù)略有下降。因此可以得出結(jié)論,啟動執(zhí)行器滾動后,磁盤使用更加均勻,避免了大量 Shuffle 數(shù)據(jù)寫入少數(shù)執(zhí)行器的情況。
CSS
本實(shí)驗(yàn)在一個(gè) 40 節(jié)點(diǎn)的集群上進(jìn)行,該集群配備了 Intel Xeon Gold 6130 CPUs @ 2.10GHz、64GB * 16 的 DRAM、16 個(gè) 13TB 的 HDD 以及 2 個(gè) 25GB 的網(wǎng)絡(luò)接口卡。
CSS 遠(yuǎn)程 Shuffle 集群部署在一個(gè) 9 節(jié)點(diǎn)的集群上,配備了 Intel Xeon Gold 6230 CPUs @ 2.10GHz、64GB * 16 的 DRAM、12 個(gè) 13TB 的 HDD 以及 2 個(gè) 25GB 的網(wǎng)絡(luò)接口卡
以確保所有 Shuffle 服務(wù)從集群中獲得類似的資源分配,實(shí)驗(yàn)也開啟了 Spark Dynamic Executor Allocation,且作業(yè) Executor 源配置相同。
我們用 1TB TPC-DS Benchmark 評估了三種 Shuffle 服務(wù):ESS、Magnet(Spark 3.2 的 Push-based Shuffle 服務(wù))和 CSS 的執(zhí)行時(shí)間和資源利用率。如上圖顯示,CSS 在某些 SQL 查詢中相比 ESS 和 Magnet 提高了超過 10% 的速度,在近 30% 的查詢中觀察到了顯著的性能提升。下表顯示 CSS 相對于 ESS 將總執(zhí)行時(shí)間減少了 0.4 小時(shí),相對于 Magnet 減少了 1.3 小時(shí)。此外,與 Magnet 和 CSS 相比,CPU 和內(nèi)存的分配和使用顯著減少。
CSS 通過在內(nèi)存中緩存分區(qū)數(shù)據(jù),并在超過塊大小閾值時(shí)刷新數(shù)據(jù)塊,從而優(yōu)于 Magnet。此外,Magnet 相比 ESS 表現(xiàn)出性能下降,因?yàn)樵诤喜⒔Y(jié)果接收和映射任務(wù)完成時(shí)的塊合并過程中,Magnet 會增加額外的等待時(shí)間,這在處理較小的 Shuffle 數(shù)據(jù)量時(shí)尤其不利。
Resource Control Evaluations
我們也評估了細(xì)粒度資源控制的效果。然而,需要注意的是,這些功能的有效性依賴于配置參數(shù)的正確設(shè)置和調(diào)優(yōu)。因此,優(yōu)化結(jié)果是兩階段配置調(diào)優(yōu)方法和這些功能的結(jié)合。這些功能提供了配置細(xì)粒度資源的能力,而兩階段配置調(diào)優(yōu)方法則最大化了這些功能的優(yōu)勢。
Resource Allocation Control
在 milliCores 實(shí)施之前,通過 Shuffle 優(yōu)化和規(guī)則配置調(diào)優(yōu),我們將約 240,000 個(gè)作業(yè)的平均 CPU 利用率提高到 56%。2023 年 8 月,我們引入 milliCores 并與參數(shù)調(diào)優(yōu)一起發(fā)布。從 2023 年 7 月到 2024 年 2 月的一批生產(chǎn)作業(yè),總數(shù)量從 21 萬 增加到 36 萬。與此同時(shí),啟用作業(yè)的數(shù)量從 0 增加到 35 萬,平均 CPU 利用率上升至 94.8%。這表明 milliCores 在提高資源效率方面發(fā)揮了關(guān)鍵作用。
如下圖所示,我們量化了 memoryBurst 實(shí)施帶來的內(nèi)存分配減少。該特性于 2023 年 10 月推出,此后啟用作業(yè)的數(shù)量逐漸從 3000+ 增加到 47 萬。到 2024 年 1 月底,實(shí)現(xiàn)了每天 55 PB·h 的內(nèi)存分配節(jié)省。這些發(fā)現(xiàn)突顯了 memoryBurst 特性在降低內(nèi)存需求和節(jié)省資源方面的有效性。
Resource Usage Control
Spill 優(yōu)化在生產(chǎn)作業(yè)中被廣泛使用,我們分析了幾個(gè)典型作業(yè),以評估帶來的改進(jìn):
- 數(shù)據(jù)倉庫作業(yè):在進(jìn)行大量 Shuffle 寫入操作的情況下,Spill 優(yōu)化使分配的內(nèi)存減少了 55%(從 3.2TB 降至 1.45TB),實(shí)際內(nèi)存使用減少了 56%(從 1.21TB 降至 0.53TB)。
- 以 Shuffle Read 為主的作業(yè):Spill 優(yōu)化顯著減少了階段中的 OOM 任務(wù)數(shù)量,從 7000+ 減少到 27,同時(shí)執(zhí)行時(shí)間從 29 分鐘減少到 11 分鐘。內(nèi)存使用量顯著下降 65%(從 23.1TB 降至 8.16TB)。
- Sort 作業(yè):內(nèi)存分配從 330TB 降至 214TB,實(shí)際內(nèi)存使用從 300TB 降至 129TB,減少了 57%。作業(yè)持續(xù)時(shí)間保持相對穩(wěn)定,從 2.1 小時(shí)變?yōu)?2.2 小時(shí)。磁盤溢出的觸發(fā)機(jī)制從僅在內(nèi)存達(dá)到滿容量時(shí)觸發(fā),轉(zhuǎn)變?yōu)橐坏﹥?nèi)存達(dá)到 1GB 閾值就定期觸發(fā)。
Configuration Tuning Evaluations
基于規(guī)則和算法的參數(shù)調(diào)優(yōu)方法都已在生產(chǎn)環(huán)境中部署。以下展示了各種調(diào)優(yōu)方法相關(guān)的種資源利用分析結(jié)果,其中利用率是通過將總資源使用量除以總資源分配量來計(jì)算的。
Rule-Based Tuning
規(guī)則基礎(chǔ)調(diào)優(yōu)方法在整個(gè)在線過程中經(jīng)過了多次迭代和優(yōu)化
- 第一階段 (2022 年 7 月)主要通過調(diào)整原有的 CPU 與內(nèi)存參數(shù)來管理作業(yè)資源分配。CPU 利用率從 2023 年 3 月前的 51% 上升到 2023 年 3 月后的 59%,內(nèi)存利用率從 43% 提升至 46%。
- 第二階段和第三階段(2023 年 8 月和 2023 年 10 月)中,隨著 milliCores 和 memoryBurst 的實(shí)施與調(diào)整,CPU 和內(nèi)存利用率顯著提升,且調(diào)優(yōu)作業(yè)數(shù)量增加。隨著規(guī)則的優(yōu)化,所有調(diào)優(yōu)作業(yè)的 CPU 利用率達(dá)到了 90%,內(nèi)存利用率達(dá)到了 52%,涵蓋了近三分之一的生產(chǎn)作業(yè),并帶來了顯著的改進(jìn)。
Algorithm-Based Tuning
算法調(diào)優(yōu)方法在生產(chǎn)環(huán)境中是作為 Rule-based 調(diào)優(yōu)的補(bǔ)充。對于一些在線 Spark 作業(yè),出于穩(wěn)定性考慮,可能不會進(jìn)行規(guī)則基礎(chǔ)調(diào)優(yōu),或者規(guī)則可能未涵蓋當(dāng)前作業(yè)的狀態(tài),因此作業(yè)可能未得到調(diào)優(yōu)或調(diào)優(yōu)效果不顯著。在這種情況下,這些作業(yè)會轉(zhuǎn)交給算法基礎(chǔ)調(diào)優(yōu),以進(jìn)一步提高資源利用率。
下圖展示了由算法基礎(chǔ)調(diào)優(yōu)調(diào)整的作業(yè)的在線性能。從 2023 年 12 月末起,算法基礎(chǔ)調(diào)優(yōu)接管了約 3000 個(gè)作業(yè)。在算法介入之前,這批作業(yè)的利用率較低,平均 CPU 和內(nèi)存利用率分別徘徊在 31% 和 21% 左右。經(jīng)過算法調(diào)優(yōu)后,這些作業(yè)的 CPU 和內(nèi)存利用率逐漸提高,最終穩(wěn)定在 58% 和 45% 左右。
Two-Stage Tuning
下圖展示了一個(gè)項(xiàng)目中所有作業(yè)的利用率變化,約 5% 的作業(yè)在 2023 年 12 月之后由算法調(diào)優(yōu)接管。因此,曲線前半部分表示項(xiàng)目僅使用規(guī)則調(diào)優(yōu)的結(jié)果,而后半部分則表示規(guī)則兩種調(diào)優(yōu)結(jié)合的結(jié)果??梢杂^察到,在僅使用規(guī)則調(diào)優(yōu)和采用兩階段組合的情況下,CPU 利用率變化不大,因?yàn)檫@批作業(yè)在使用規(guī)則調(diào)優(yōu)時(shí)已經(jīng)具有較高的 CPU 利用率。然而在內(nèi)存利用率方面,前者約為 21%,而后者約為 26%,有顯著的改進(jìn)。這是因?yàn)檫@ 5% 的作業(yè)內(nèi)存使用比例相對較高,經(jīng)過算法進(jìn)一步優(yōu)化后,內(nèi)存利用率顯著提升。
算法調(diào)優(yōu)可以進(jìn)一步提升規(guī)則調(diào)優(yōu)獲得的結(jié)果。然而,由于其時(shí)間消耗大且調(diào)優(yōu)速度較慢,算法基礎(chǔ)調(diào)優(yōu)需要與規(guī)則基礎(chǔ)調(diào)優(yōu)結(jié)合使用,以在生產(chǎn)環(huán)境中實(shí)現(xiàn)更好的效果。
Overall Tuning Performance
本文提出的技術(shù)經(jīng)過了廣泛的迭代、優(yōu)化和部署。我們將通過兩年的數(shù)據(jù)對這些技術(shù)在提升字節(jié)跳動大規(guī)模 Spark 工作負(fù)載資源效率方面的效果進(jìn)行了統(tǒng)計(jì)分析。
上圖展示了 2022 年至 2023 年所有 Spark 作業(yè)資源效率的提升情況??傮w提升情況如下:
- 在 CPU 利用率方面,通過規(guī)則配置調(diào)優(yōu)的迭代優(yōu)化,利用率從 2022 年的 48% 提升到 2023 年的 60%,隨后隨著 milliCores 特性的引入,利用率進(jìn)一步上升到超過 70%;
- 內(nèi)存利用率通過規(guī)則配置調(diào)優(yōu)從 2022 年的 43.3% 提高到 46%,并通過 memoryBurst 特性進(jìn)一步提升至接近 50%;
- Shuffle 方面,Shuffle Block Ratio,表示等待 Shuffle 的時(shí)間比例,最初在 2022 年 1 月約為 14%,通過 Enhanced ESS 和 CSS 在 Shuffle 速度和穩(wěn)定性方面的增強(qiáng)減少到約 4%-6%,隨后通過參數(shù)調(diào)優(yōu)進(jìn)一步降低至 2%。
整體上,我們服務(wù)的用戶數(shù)量從 9000+ 增加到 1.4 萬,優(yōu)化的作業(yè)數(shù)量從 25 萬激增至 53 萬。相應(yīng)地,CPU 利用率超過 60% 的作業(yè)的日均數(shù)量從 2023 年 3 月的 15 萬 增加到 2024 年 2 月的 30 萬 以上,而內(nèi)存利用率超過 50% 的作業(yè)也達(dá)到了約 15 萬。CPU 和內(nèi)存資源分配的日均節(jié)省峰值分別達(dá)到 100 萬 核/日和 4.6 PB/日。此外,作業(yè)執(zhí)行時(shí)間也顯著減少,在 2024 年 2 月減少了約 11 分鐘,占此前平均作業(yè)執(zhí)行時(shí)間的 31%。
作者介紹:
- 程航,畢業(yè)于新加坡國立大學(xué),21年加入字節(jié),現(xiàn)任字節(jié)跳動大數(shù)據(jù)開發(fā)工程師,專注大數(shù)據(jù)分布式計(jì)算領(lǐng)域,主要負(fù)責(zé) Spark 內(nèi)核開發(fā)及字節(jié)自研 Shuffle Service 開發(fā),現(xiàn)主要負(fù)責(zé)分布式機(jī)器學(xué)習(xí)框架相關(guān)的開發(fā)。
- 魏中佳,畢業(yè)于電子科技大學(xué),18年加入字節(jié),現(xiàn)任字節(jié)跳動大數(shù)據(jù)開發(fā)工程師,專注大數(shù)據(jù)分布式計(jì)算領(lǐng)域,主要負(fù)責(zé) Spark 內(nèi)核開發(fā)及字節(jié)自研 Shuffle Service 開發(fā),現(xiàn)主要負(fù)責(zé)數(shù)據(jù)湖相關(guān)的開發(fā)。