在 Apache Spark 這類分散式運算框架中,實現卓越的處理效能不僅僅是堆疊硬體資源。真正的優化來自於對計算資源與資料特性之間深層互動的理解。本篇文章從兩大核心維度切入,首先探討計算資源的精細配置,分析執行器、記憶體與核心數的黃金比例,以及磁碟 I/O 和網路頻寬如何成為大型 Shuffle 操作的關鍵瓶頸。接著,文章將焦點轉向資料層面,闡述資料傾斜(Data Skew)如何無聲地侵蝕系統效率,並說明索引與分區策略對於最小化資料掃描、提升查詢速度的根本性重要。透過對硬體配置與資料布局的雙重調校,工程師才能在複雜的工作負載中,尋求性能與成本效益的最佳平衡點。
第十一章:性能調優
合理配置計算資源
執行器、核心和記憶體
執行器可以配置不同的核心和記憶體分配。然而,絕大多數用例可以採取一種簡化的方法,即執行器數量與核心數量的比例相同。通常不值得花費精力去配置分配給執行器的更多核心,時間最好花在其他優化上。此類別中最好的優化是記憶體與核心的比例。對於大型Shuffle工作負載,較高的記憶體與核心比例有利於避免Shuffle溢出到磁碟。這會導致資料必須經過序列化過程,並顯著減慢處理時間。
磁碟
磁碟組件主要在大型Shuffle操作(連接、聚合等)中發揮作用。當集群中沒有足夠的記憶體時,資料將溢出到磁碟。這是會減慢管道速度的最大問題之一,但在非常大的Shuffle(例如兩個大表之間的連接)期間有時是不可避免的。磁碟儲存可以是網路連接的,也可以是物理連接的。網路儲存的優點是能夠彈性調整大小以適應意外的較大容量,並且通常更便宜。缺點是I/O會較慢。物理連接儲存的大小是固定的,成本更高,但優點是I/O速度顯著更快。對於一般工作負載,網路連接儲存就足夠了,當已知工作負載會定期飆升時,應啟用它以彈性擴展。當已知會發生磁碟溢出時,選擇物理連接儲存將是正確的決定。
網路
在雲端中,網路可以提供一定的吞吐量或專用頻寬。這個組件的選擇將影響從物件儲存讀取資料以及集群中的Shuffle效率。專用頻寬將允許不間斷的快速網路速度,這對於大型Shuffle操作至關重要。
性能調優
228
監控和工具
玄貓使用兩組主要工具來監控玄貓的Spark應用程式和前面提到的組件:
Spark UI:這提供了有關作業階段、任務、執行器記憶體和Shuffle的全面詳細資訊。- 外部監控工具:
Grafana、Prometheus和Datadog等工具可以提供有關JVM指標、CPU、記憶體使用情況等的見解。
合理配置Apache Spark的計算資源是藝術與科學的結合。雖然上述指南提供了一種結構化方法,但通常特定用例的細微差別需要迭代測試和調優。始終監控玄貓的Spark應用程式,隨時準備適應,並努力在性能和成本效益之間取得平衡。
在下一節中,玄貓將從資料角度討論優化。
理解資料傾斜、索引和分區
就像任何資料處理系統一樣,所有最好的硬體只會產生平庸的結果。沒有什麼靈丹妙藥可以解決不良的資料布局。最快的磁碟、處理晶片和網路都不能消除規劃深思熟慮的索引和分區策略的必要性。資料傾斜會悄悄地進入處理管道或查詢,並使其緩慢運行。這三個關鍵方面需要規劃和監控,以防止資料處理和查詢的性能下降。玄貓將在以下部分了解更多關於它們的資訊。
資料傾斜
資料傾斜是使用Apache Spark等分散式資料系統時的常見問題。當某些處理分區顯著大於其他分區時,它會出現,導致某些任務快速完成,而等待其他任務完成。這可能導致計算資源利用不足、處理時間過長和記憶體不足錯誤。連接(Joins)是受資料傾斜影響最常見的任務之一,也是最難發現和排除故障的任務。傾斜的發生是因為連接鍵的計數顯著高於其他連接鍵,並且Spark會將連接兩側的資料針對給定鍵共同定位在單個執行器上。如果其中一個鍵要處理的值比其他執行器多一個數量級,它將會滯後,因為它有更多的工作要做。
此圖示:Spark 資源配置與性能優化要素
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
actor "資料工程師" as DataEngineer
package "Spark 應用程式" as SparkApp {
rectangle "Spark 工作負載" as Workload
}
package "計算資源組件" as ComputeResources {
rectangle "執行器 (Executor)" as Executor
rectangle "核心 (Core)" as Core
rectangle "記憶體 (Memory)" as Memory
rectangle "磁碟 (Disk)" as Disk
rectangle "網路 (Network)" as Network
}
package "性能瓶頸與挑戰" as Bottlenecks {
rectangle "Shuffle 溢出到磁碟" as ShuffleSpill
rectangle "I/O 速度限制" as IOSpeedLimit
rectangle "網路吞吐量不足" as NetworkThroughput
rectangle "資料傾斜 (Data Skew)" as DataSkewProblem
rectangle "低效索引/分區" as InefficientIndexPartition
}
package "性能優化策略" as OptimizationStrategies {
rectangle "記憶體與核心比例優化" as MemCoreRatio
rectangle "磁碟類型選擇" as DiskTypeSelection
rectangle "網路頻寬配置" as NetworkBandwidth
rectangle "資料傾斜處理技術" as DataSkewHandling
rectangle "索引與分區策略" as IndexPartitionStrategy
}
DataEngineer --> Workload : 運行 Spark 工作負載
Workload --> Executor : 消耗執行器資源
Workload --> Memory : 消耗記憶體
Workload --> Core : 消耗核心
Workload --> Disk : 使用磁碟進行溢出
Workload --> Network : 進行資料傳輸
Memory --> ShuffleSpill : 記憶體不足導致 Shuffle 溢出
Disk --> IOSpeedLimit : 磁碟 I/O 速度影響性能
Network --> NetworkThroughput : 網路是 Shuffle 和讀取瓶頸
ShuffleSpill --> MemCoreRatio : 調整記憶體與核心比例避免溢出
IOSpeedLimit --> DiskTypeSelection : 選擇物理連接磁碟提升 I/O
NetworkThroughput --> NetworkBandwidth : 配置專用頻寬解決網路瓶頸
Workload --> DataSkewProblem : 工作負載中可能存在資料傾斜
Workload --> InefficientIndexPartition : 缺乏良好索引分區策略
DataSkewProblem --> DataSkewHandling : 處理資料傾斜
InefficientIndexPartition --> IndexPartitionStrategy : 規劃索引與分區
OptimizationStrategies --> DataEngineer : 工程師實施優化
note right of Executor
- JVM 進程,執行任務
- 數量和配置影響並行度
end note
note right of Memory
- 影響 Shuffle 溢出和緩存效率
- 高記憶體/核心比有利於大型 Shuffle
end note
note right of Disk
- 網路連接 vs. 物理連接
- 成本與 I/O 速度的權衡
end note
note right of Network
- 影響資料讀取和 Shuffle 效率
- 專用頻寬對大型 Shuffle 至關重要
end note
note right of DataSkewProblem
- 分區大小不均導致任務滯後
- 常見於 Join 操作
end note
@enduml
看圖說話:
此圖示深入剖析了Spark應用程式在運行時所涉及的計算資源組件,以及如何針對這些組件和常見性能瓶頸實施優化策略。它強調了硬體資源與資料處理策略之間密不可分的關係。
Spark工作負載的執行依賴於多個計算資源組件:
- 執行器(Executor):作為
JVM進程,負責實際的任務執行,其數量和配置直接影響並行度。 - 記憶體(Memory):對
Shuffle溢出和緩存效率至關重要,尤其對於大型Shuffle工作負載,高記憶體與核心比例能有效避免資料溢出到磁碟。 - 核心(Core):執行器的計算單元,與記憶體共同決定了執行器的處理能力。
- 磁碟(Disk):用於處理記憶體溢出的資料,其類型(網路連接或物理連接)在成本與I/O速度之間存在權衡。
- 網路(Network):影響資料讀取速度和Shuffle效率,專用頻寬對於大型Shuffle操作至關重要。
在實際運行中,這些資源可能導致多種性能瓶頸:
- Shuffle溢出到磁碟:當記憶體不足時發生,顯著降低性能。
- I/O速度限制:磁碟類型和配置不當可能導致。
- 網路吞吐量不足:影響資料從物件儲存的讀取和節點間Shuffle。
- 資料傾斜(Data Skew):分區大小不均導致某些任務滯後,尤其常見於
Join操作。 - 低效索引/分區:缺乏深思熟慮的資料布局策略。
為了解決這些問題,資料工程師需要實施一系列性能優化策略:
- 記憶體與核心比例優化:調整執行器配置以適應工作負載,避免
Shuffle溢出。 - 磁碟類型選擇:根據溢出頻率和I/O需求,選擇網路連接或物理連接磁碟。
- 網路頻寬配置:確保有足夠的網路頻寬支持高效的資料傳輸和
Shuffle。 - 資料傾斜處理技術:採用如鹽值化、廣播Join或自定義分區等方法來平衡資料分佈。
- 索引與分區策略:規劃和監控資料的索引和分區,以優化查詢性能和減少
Shuffle。
總之,Spark性能調優是一個多維度的過程,需要資料工程師深入理解計算資源的特性,並結合資料本身的特性(如資料傾斜),透過迭代測試和監控來實現性能與成本效益之間的最佳平衡。
第十一章:性能調優
理解資料傾斜、索引和分區
通常,資料傾斜會在開發階段被發現,但也有很多時候傾斜會出現在生產環境中。當某個產品比其他產品更受歡迎時,訂單表可能會更偏重於某個特定商品編號。季節性湧入可能導致某些產品出現短暫的飆升,這可能導致在關鍵銷售季節期間管道延遲。捕捉這些影響或完全避免它們對於健康的管道至關重要。
理解資料傾斜、索引和分區
229
識別資料傾斜可以透過Spark UI完成。透過觀察任務執行時間,工程師可以看到大多數任務完成得很快,而其他任務的執行時間是它們的兩倍以上。解決資料傾斜的一些技術如下:
- 廣播連接(Broadcast join)
- 加鹽(Salting)
- 自適應查詢執行(Adaptive Query Execution, AQE)
如果要連接的表足夠小以至於可以放入記憶體中,則可以使用廣播連接。這些表將被發送到每個執行器,並消除Shuffle較大表的需要,因為每個執行器都有一個副本用於比較。這是一種非常高效的策略,但它也有許多缺點。並非所有表都足夠小以進行廣播,這可能會嚴重限制此策略的使用。廣播表的大小也可能不可預測,使管道處於不可預測的狀態。僅當表相對靜態時才使用廣播技術。
加鹽涉及向導致資料傾斜的鍵添加一個隨機值或鹽。透過在鍵中添加一個隨機字尾,玄貓本質上是創建了相同鍵的多個副本,這有助於將資料更均勻地分佈在各個分區中。以下範例顯示了連接DataFrame的傾斜分佈:
df1:
+-------+------+
| userID|value1|
+-------+------+
| 123| A|
| 123| B|
| 123| C|
| 456| D|
| 789| E|
+-------+------+
df2:
+-------+------+
| userID|value2|
+-------+------+
| 123| X|
| 123| Y|
| 456| Z|
+-------+------+
性能調優
230
工程師可以向連接鍵添加鹽以更好地分佈它們,同時保持相同的結果,而不是執行傾斜連接:
df1 (加鹽後):
+-------+------+
| userID|value1|
+-------+------+
| 123_1| A|
| 123_2| B|
| 123_3| C|
| 456| D|
| 789| E|
+-------+------+
df2 (加鹽後):
+-------+------+
| userID|value2|
+-------+------+
| 123_1| X|
| 123_2| X|
| 123_3| X|
| 123_1| Y|
| 123_2| Y|
| 123_3| Y|
| 456| Z|
+-------+------+
幸運的是,在Spark 3.x中,AQE引入了在大多數情況下自動檢測和處理傾斜連接的能力。請參閱圖11.3以了解如何在Spark UI中發現這一點:
圖11.5 – 自動傾斜連接檢測
如果管道正在使用Spark 3.x,那麼大多數時候,資料傾斜可以被視為次要問題。總的來說,監控管道並在作業完成時間超出預期時發出警報是一個好主意。運營團隊可以針對這些情況採取適當的行動,以避免發生更大的中斷。
理解資料傾斜、索引和分區
231
索引和分區
當使用Spark處理資料管道時,絕大多數資料都存在於雲端的某種物件儲存中。就像在傳統資料庫中一樣,高效查詢的關鍵在於能夠讀取最少量的資料來解決查詢或轉換。
在資料湖或湖倉一體中,將資料共同定位在一起的能力將是限制掃描的驅動力。如果沒有這種共同定位,查詢最終將對表中的所有檔案執行全掃描以滿足查詢。
Delta Lake目前提供了兩個實現此目標的核心機制。分區是一種行之有效的方法,用於將相同的資料值發送到同一組檔案。當使用低基數列作為分區列時,這非常有用,例如日期、傳感器ID或有時是產品ID。Z-ordering相當於集群索引,是將資料共同定位在同一組檔案中的第二種方法。此技術可用於低基數或高基數列。
分區是實現粗粒度資料跳過的絕佳技術。它通常與日期欄位(不帶時間戳)一起使用,因為資料集中只有有限數量的天數,並且它是運行查詢時常見的過濾列。
此圖示:資料傾斜的診斷與緩解策略
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
actor "資料工程師" as DataEngineer
package "Spark 應用程式" as SparkApp {
rectangle "資料處理管道" as DataPipeline
}
package "資料傾斜問題" as DataSkewProblem {
rectangle "分區大小不均" as UnevenPartitions
rectangle "任務執行時間差異大" as TaskTimeVariance
rectangle "計算資源利用不足" as UnderutilizedCompute
rectangle "記憶體不足錯誤 (OOM)" as OOMErrors
rectangle "Join 操作瓶頸" as JoinBottleneck
}
package "診斷工具與方法" as Diagnosis {
rectangle "Spark UI (Stages/Tasks Tab)" as SparkUI
rectangle "監控警報系統" as MonitoringAlerts
}
package "資料傾斜緩解策略" as MitigationStrategies {
rectangle "廣播連接 (Broadcast Join)" as BroadcastJoin
rectangle "加鹽 (Salting)" as Salting
rectangle "自適應查詢執行 (AQE)" as AQE
}
package "資料布局優化" as DataLayoutOptimization {
rectangle "索引 (Indexing)" as Indexing
rectangle "分區 (Partitioning)" as Partitioning
rectangle "Z-ordering" as ZOrdering
}
DataEngineer --> DataPipeline : 設計與運行資料管道
DataPipeline --> DataSkewProblem : 管道可能遭遇資料傾斜
DataSkewProblem --> UnevenPartitions : 表現為分區大小不均
UnevenPartitions --> TaskTimeVariance : 導致任務執行時間差異大
TaskTimeVariance --> UnderutilizedCompute : 進而造成計算資源浪費
UnderutilizedCompute --> OOMErrors : 嚴重時引發記憶體不足
JoinBottleneck --> DataSkewProblem : Join 是資料傾斜常見來源
DataEngineer --> Diagnosis : 診斷資料傾斜
Diagnosis --> SparkUI : 透過 Spark UI 觀察任務執行時間
Diagnosis --> MonitoringAlerts : 設定警報以即時發現異常
SparkUI --> TaskTimeVariance : Spark UI 可視化任務時間差異
MitigationStrategies --> DataEngineer : 工程師實施緩解策略
BroadcastJoin --> DataSkewProblem : 適用於小表 Join
Salting --> DataSkewProblem : 透過增加隨機值分散熱點鍵
AQE --> DataSkewProblem : Spark 3.x 自動處理傾斜 Join
DataLayoutOptimization --> DataPipeline : 預先優化資料布局
Indexing --> DataLayoutOptimization : 減少掃描量
Partitioning --> DataLayoutOptimization : 粗粒度資料跳過 (日期, ID)
ZOrdering --> DataLayoutOptimization : 集群索引,精細化共同定位
note right of DataSkewProblem
- 分散式系統常見問題
- 導致性能下降和資源浪費
- 難以發現和排除故障
end note
note right of BroadcastJoin
- 小表載入到所有 Executor 記憶體
- 避免大表 Shuffle
- 適用於靜態小表
end note
note right of Salting
- 在熱點 Join Key 上添加隨機字尾
- 均勻分散資料到不同分區
end note
note right of AQE
- Spark 3.x 新特性
- 自動檢測
### 結論
縱觀現代資料工程的性能挑戰,Apache Spark 的調優已從單純的資源配置,演進為一門結合硬體、軟體與資料特性的精密藝術。單純追求更快的磁碟或更大的記憶體,往往會陷入邊際效益遞減的陷阱;真正的性能瓶頸,如資料傾斜或不當的 Shuffle,其根源常在於資料布局本身。相較於傳統被動應對傾斜的「加鹽」或廣播連接等戰術手段,現代優化更強調透過合理分區與 Z-ordering 進行主動預防,這種從「救火」到「建構防火通道」的思維轉變,才是區分資深與初階工程師的關鍵。
隨著 Spark 3.x 的自適應查詢執行(AQE)等智慧功能日趨成熟,我們預見未來的性能調優將更加自動化。工程師的價值將從繁瑣的手動參數調整,轉向更高層次的架構設計與資料治理,專注於建立一個具備自我修復與優化能力的彈性資料平台。
玄貓認為,卓越的性能調優已非資源的線性堆疊,而是涵蓋硬體權衡、資料洞察與框架理解的系統性工程,它深刻考驗著團隊在成本與效率之間取得動態平衡的架構智慧。