Flink性能調優小小總結

點擊上方 藍色字體 ,選擇“ 設爲星標
回覆”資源“獲取更多資源

1 配置內存

操作場景
Flink是依賴內存計算,計算過程中內存不夠對Flink的執行效率影響很大。可以通過監控GC(Garbage Collection),評估內存使用及剩餘情況來判斷內存是否變成性能瓶頸,並根據情況優化。
監控節點進程的YARN的Container GC日誌,如果頻繁出現Full GC,需要優化GC。
GC的配置:在客戶端的"conf/flink-conf.yaml"配置文件中,在“env.java.opts”配置項中添加參數:

      
      
      
-Xloggc:<LOG_DIR>/gc.log
-XX:+PrintGCDetails
-XX:-OmitStackTraceInFastThrow
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=20
-XX:GCLogFileSize=20M
此處默認已經添加GC日誌。
操作步驟
  • 優化GC

調整老年代和新生代的比值。在客戶端的“conf/flink-conf.yaml”配置文件中,在“env.java.opts”配置項中添加參數:“-XX:NewRatio”。如“ -XX:NewRatio=2”,則表示老年代與新生代的比值爲2:1,新生代佔整個堆空間的1/3,老年代佔2/3。
  • 開發Flink應用程序時,優化DataStream的數據分區或分組操作。

當分區導致數據傾斜時,需要考慮優化分區。避免非並行度操作,有些對DataStream的操作會導致無法並行,例如WindowAll。keyBy儘量不要使用String。

2 設置並行度

操作場景
並行度控制任務的數量,影響操作後數據被切分成的塊數。調整並行度讓任務的數量和每個任務處理的數據與機器的處理能力達到最優。查看CPU使用情況和內存佔用情況,當任務和數據不是平均分佈在各節點,而是集中在個別節點時,可以增大並行度使任務和數據更均勻的分佈在各個節點。增加任務的並行度,充分利用集羣機器的計算能力,一般並行度設置爲集羣CPU核數總和的2-3倍。
操作步驟
任務的並行度可以通過以下四種層次(按優先級從高到低排列)指定,用戶可以根據實際的內存、CPU、數據以及應用程序邏輯的情況調整並行度參數。
  • 算子層次

一個算子、數據源和sink的並行度可以通過調用setParallelism()方法來指定,例如

      
      
      
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<String> text = [...]
DataStream<Tuple2<String, Integer>> wordCounts = text
.flatMap(new LineSplitter())
.keyBy(0)
.timeWindow(Time.seconds(5))
.sum(1).setParallelism(5);
wordCounts.print();
env.execute("Word Count Example");
  • 執行環境層次

Flink程序運行在執行環境中。執行環境爲所有執行的算子、數據源、data sink定義了一個默認的並行度。
執行環境的默認並行度可以通過調用setParallelism()方法指定。例如:

      
      
      
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(3);
DataStream<String> text = [...]
DataStream<Tuple2<String, Integer>> wordCounts = [...]
wordCounts.print();
env.execute("Word Count Example");
  • 客戶端層次

並行度可以在客戶端將job提交到Flink時設定。對於CLI客戶端,可以通過“-p”參數指定並行度。例如:./bin/flink run -p 10 ../examples/WordCount-java.jar
  • 系統層次

在系統級可以通過修改Flink客戶端conf目錄下的“flink-conf.yaml”文件中的“parallelism.default”配置選項來指定所有執行環境的默認並行度。

3.配置進程參數

操作場景
Flink on YARN模式下,有JobManager和TaskManager兩種進程。在任務調度和運行的過程中,JobManager和TaskManager承擔了很大的責任。
因而JobManager和TaskManager的參數配置對Flink應用的執行有着很大的影響意義。用戶可通過如下操作對Flink集羣性能做優化。
操作步驟
1.配置JobManager內存
JobManager負責任務的調度,以及TaskManager、RM之間的消息通信。當任務數變多,任務平行度增大時,JobManager內存都需要相應增大。您可以根據實際任務數量的多少,爲JobManager設置一個合適的內存。
  • 在使用yarn-session命令時,添加“-jm MEM”參數設置內存。

  • 在使用yarn-cluster命令時,添加“-yjm MEM”參數設置內存。

2.配置TaskManager個數
每個TaskManager每個核同時能跑一個task,所以增加了TaskManager的個數相當於增大了任務的併發度。在資源充足的情況下,可以相應增加TaskManager的個數,以提高運行效率。
  • 在使用yarn-session命令時,添加“-n NUM”參數設置TaskManager個數。

  • 在使用yarn-cluster命令時,添加“-yn NUM”參數設置TaskManager個數。

3.配置TaskManager Slot數
每個TaskManager多個核同時能跑多個task,相當於增大了任務的併發度。但是由於所有核共用TaskManager的內存,所以要在內存和核數之間做好平衡。
  • 在使用yarn-session命令時,添加“-s NUM”參數設置SLOT數。

  • 在使用yarn-cluster命令時,添加“-ys NUM”參數設置SLOT數。

4.配置TaskManager內存
TaskManager的內存主要用於任務執行、通信等。當一個任務很大的時候,可能需要較多資源,因而內存也可以做相應的增加。
  • 將在使用yarn-sesion命令時,添加“-tm MEM”參數設置內存。

  • 將在使用yarn-cluster命令時,添加“-ytm MEM”參數設置內存。

設計分區方法

操作場景
合理的設計分區依據,可以優化task的切分。在程序編寫過程中要儘量分區均勻,這樣可以實現每個task數據不傾斜,防止由於某個task的執行時間過長導致整個任務執行緩慢。
操作步驟
以下是幾種分區方法
  • 隨機分區:將元素隨機地進行分區。dataStream.shuffle();

  • Rebalancing (Round-robin partitioning):基於round-robin對元素進行分區,使得每個分區負責均衡。對於存在數據傾斜的性能優化是很有用的。dataStream.rebalance();

  • Rescaling:以round-robin的形式將元素分區到下游操作的子集中。如果你想要將數據從一個源的每個並行實例中散發到一些mappers的子集中,用來分散負載,但是又不想要完全rebalance 介入(引入rebalance()),這會非常有用。dataStream.rescale();

  • 廣播:廣播每個元素到所有分區。dataStream.broadcast();

  • 自定義分區:使用一個用戶自定義的Partitioner對每一個元素選擇目標task,由於用戶對自己的數據更加熟悉,可以按照某個特徵進行分區,從而優化任務執行。簡單示例如下所示:


      
      
      
// fromElements構造簡單的Tuple2流
DataStream<Tuple2<String, Integer>> dataStream = env.fromElements(Tuple2.of("hello",1), Tuple2.of("test",2), Tuple2.of("world",100));
// 定義用於分區的key值,返回即屬於哪個partition的,該值加1就是對應的子任務的id號
Partitioner<Tuple2<String, Integer>> strPartitioner = new Partitioner<Tuple2<String, Integer>>() {
@Override
public int partition(Tuple2<String, Integer> key, int numPartitions) {
return (key.f0.length() + key.f1) % numPartitions;
}
};
// 使用Tuple2進行分區的key值
dataStream.partitionCustom(strPartitioner, new KeySelector<Tuple2<String, Integer>, Tuple2<String, Integer>>() {
@Override
public Tuple2<String, Integer> getKey(Tuple2<String, Integer> value) throws Exception {
return value;
}
}).print();

配置netty網絡通信

操作場景
Flink通信主要依賴netty網絡,所以在Flink應用執行過程中,netty的設置尤爲重要,網絡通信的好壞直接決定着數據交換的速度以及任務執行的效率。
操作步驟
以下配置均可在客戶端的“conf/flink-conf.yaml”配置文件中進行修改適配,默認已經是相對較優解,請謹慎修改,防止性能下降。
•“taskmanager.network.netty.num-arenas”:默認是“taskmanager.numberOfTaskSlots”,表示netty的域的數量。 
•“taskmanager.network.netty.server.numThreads”和“taskmanager.network.netty.client.numThreads”:默認是“taskmanager.numberOfTaskSlots”,表示netty的客戶端和服務端的線程數目設置。 
•“taskmanager.network.netty.client.connectTimeoutSec”:默認是120s,表示taskmanager的客戶端連接超時的時間。
  • “taskmanager.network.netty.sendReceiveBufferSize”:默認是系統緩衝區大小(cat /proc/sys/net/ipv4/tcp _ [rw]mem) ,一般爲4MB,表示netty的發送和接收的緩衝區大小。

  • “taskmanager.network.netty.transport”:默認爲“nio”方式,表示netty的傳輸方式,有“nio”和“epoll”兩種方式。

解決數據傾斜

當數據發生傾斜(某一部分數據量特別大),雖然沒有GC(Gabage Collection,垃圾回收),但是task執行時間嚴重不一致。
  • 需要重新設計key,以更小粒度的key使得task大小合理化。

  • 修改並行度。

  • 調用rebalance操作,使數據分區均勻。

緩衝區超時設置
  • 由於task在執行過程中存在數據通過網絡進行交換,數據在不同服務器之間傳遞的緩衝區超時時間可以通過setBufferTimeout進行設置。

  • 當設置“setBufferTimeout(-1)”,會等待緩衝區滿之後纔會刷新,使其達到最大吞吐量;當設置“setBufferTimeout(0)”時,可以最小化延遲,數據一旦接收到就會刷新;當設置“setBufferTimeout”大於0時,緩衝區會在該時間之後超時,然後進行緩衝區的刷新。示例可以參考如下:env.setBufferTimeout(timeoutMillis); env.generateSequence(1,10).map(new MyMapper()).setBufferTimeout(timeoutMillis);

Checkpoint 調優

1.什麼是 checkpoint 簡單地說就是 Flink 爲了達到容錯和 exactly-once 語義的功能,定期把 state 持久化下來,而這一持久化的過程就叫做 checkpoint ,它是 Flink Job 在某一時刻全局狀態的快照。
當我們要對分佈式系統實現一個全局狀態保留的功能時,傳統方案會引入一個統一時鐘,通過分佈式系統中的 master 節點廣播出去給每一個 slaves 節點,當節點接收到這個統一時鐘時,它們就記錄下自己當前的狀態即可。

但是統一時鐘的方式也存在一定的問題,某一個 node 進行的 GC 時間比較長,或者 master 與 slaves 的網絡在當時存在波動而造成時鐘的發送延遲或者發送失敗,都會造成此 slave 和其它的機器出現數據不一致而最終導致腦裂的情況。如果我們想要解決這個問題,就需要對 master 和 slaves 做一個 HA(High Availability)。但是,一個系統越是複雜,就越不穩定且維護成本越高。
Flink 是將 checkpoint 都放進了一個名爲 Barrier 的流。

上圖中就是一個 Barrier 的例子,從上游的第一個 Task 到下游的最後一個 Task,每次當 Task 經過圖中藍色的柵欄時,就會觸發 save snapshot(快照)的功能。我們用一個例子來簡單說明。
2.實例分析

這是一個簡單的 ETL 過程,首先我們把數據從 Kafka 中拿過來進行一個 trans 的轉換操作,然後再發送到一個下游的 Kafka。
此時這個例子中沒有進行 chaining 的調優。所以此時採用的是 forward strategy ,也就是 “一個 task 的輸出只發送給一個 task 作爲輸入”,這樣的方式,這樣做也有一個好處就是如果兩個 task 都在一個 JVM 中的話,那麼就可以避免不必要的網絡開銷。
設置 Parallism 爲 2,此時的 DAG 圖如下:

■ CK的分析過程

5 CK 的分析過程

每一個 Flink 作業都會有一個 JobManager ,JobManager 裏面又會有一個 checkpoint coordinator 來管理整個 checkpoint 的過程,我們可以設置一個時間間隔讓 checkpoint coordinator 將一個 checkpoint 的事件發送給每一個 Container 中的 source task,也就是第一個任務(對應並行圖中的 task1,task2)。
當某個 Source 算子收到一個 Barrier 時,它會暫停自身的數據處理,然後將自己的當前 state 製作成 snapshot(快照),並保存到指定的持久化存儲中,最後向 CheckpointCoordinator 異步發送一個 ack(Acknowledge character --- 確認字符),同時向自身所有下游算子廣播該 Barrier 後恢復自身的數據處理。
每個算子按照上面不斷製作 snapshot 並向下遊廣播,直到最後 Barrier 傳遞到 sink 算子,此時快照便製作完成。這時候需要注意的是,上游算子可能是多個數據源,對應多個 Barrier 需要全部到齊才一次性觸發 checkpoint ,所以在遇到 checkpoint 時間較長的情況時,有可能是因爲數據對齊需要耗費的時間比較長所造成的。
■ Snapshot & Recover

如圖,這是我們的Container容器初始化的階段,e1 和 e2 是剛從 Kafka 消費過來的數據,與此同時,CheckpointCoordinator 也往它發送了 Barrier。
此時 Task1 完成了它的 checkpoint 過程,效果就是記錄下 offset 爲2(e1,e2),然後把 Barrier 往下游的算子廣播,Task3 的輸入爲 Task1 的輸出,現在假設我的這個程序的功能是統計數據的條數,此時 Task3 的 checkpoint 效果就是就記錄數據數爲2(因爲從 Task1 過來的數據就是 e1 和 e2 兩條),之後再將 Barrier 往下廣播,當此 Barrier 傳遞到 sink 算子,snapshot 就算是製作完成了。
此時 source 中還會源源不斷的產生數據,併產生新的 checkpoint ,但是此時如果 Container 宕機重啓就需要進行數據的恢復了。剛剛完成的 checkpoint 中 offset爲2,count爲2,那我們就按照這個 state 進行恢復。此時 Task1 會從 e3 開始消費,這就是 Recover 操作。
■ checkpoint 的注意事項

下面列舉的3個注意要點都會影響到系統的吞吐,在實際開發過程中需要注意:
一下內容爲Flink中文社區的總結,供大家參考:

Flink 作業的問題定位

1.問題定位口訣
“一壓二查三指標,延遲吞吐是核心。時刻關注資源量 , 排查首先看GC。”
一壓是指背壓,遇到問題先看背壓的情況,二查就是指 checkpoint ,對齊數據的時間是否很長,state 是否很大,這些都是和系統吞吐密切相關的,三指標就是指 Flink UI 那塊的一些展示,我們的主要關注點其實就是延遲和吞吐,系統資源,還有就是 GC logs。
  • 看反壓 :通常最後一個被壓高的 subTask 的下游就是 job 的瓶頸之一。

  • 看 Checkpoint 時長 :Checkpoint 時長能在一定程度影響 job 的整體吞吐。

  • 看核心指標 :指標是對一個任務性能精準判斷的依據,延遲指標和吞吐則是其中最爲關鍵的指標。

  • 資源的使用率:提高資源的利用率是最終的目的。

■ 常見的性能問題

簡單解釋一下:
  1. 在關注背壓的時候大家往往忽略了數據的序列化和反序列化,過程所造成的性能問題。

  2. 一些數據結構 ,比如 HashMap 和 HashSet 這種 key 需要經過 hash 計算的數據結構,在數據量大的時候使用 keyby 進行操作, 造成的性能影響是非常大的。

  3. 數據傾斜 是我們的經典問題,後面再進行展開。

  4. 如果我們的下游是 MySQL,HBase這種,我們都會進行一個批處理的操作,就是讓數據存儲到一個 buffer 裏面,在達到某些條件的時候再進行發送,這樣做的目的就是減少和外部5. 系統的交互,降低 網絡開銷 的成本。

  5. 頻繁GC ,無論是 CMS 也好,G1也好,在進行 GC 的時候,都會停止整個作業的運行,GC 時間較長還會導致 JobManager 和 TaskManager 沒有辦法準時發送心跳,此時 JobManager 就會認爲此 TaskManager 失聯,它就會另外開啓一個新的 TaskManager

  6. 窗口是一種可以把無限數據切割爲有限數據塊的手段。比如我們知道,使用滑動窗口的時候數據的重疊問題,size = 5min 雖然不屬於大窗口的範疇,可是 step = 1s 代表1秒就要進行一次數據的處理,這樣就會造成數據的重疊很高,數據量很大的問題。

2.Flink 作業調優

我們可以通過一些數據結構,比如 Set 或者 Map 來結合 Flink state 進行去重。但是這些去重方案會隨着數據量不斷增大,從而導致性能的急劇下降,比如剛剛我們分析過的 hash 衝突帶來的寫入性能問題,內存過大導致的 GC 問題,TaskManger 的失聯問題。

方案二和方案三也都是通過一些數據結構的手段去進行去重,有興趣的同學可以自行下去了解,在這裏不再展開。
■ 數據傾斜

數據傾斜是大家都會遇到的高頻問題,解決的方案也不少。
第一種場景是當我們的併發度設置的比分區數要低時,就會造成上面所說的消費不均勻的情況。

第二種提到的就是 key 分佈不均勻的情況,可以通過添加隨機前綴打散它們的分佈,使得數據不會集中在幾個 Task 中。
在每個節點本地對相同的 key 進行一次聚合操作,類似於 MapReduce 中的本地 combiner。map-side 預聚合之後,每個節點本地就只會有一條相同的 key,因爲多條相同的 key 都被聚合起來了。其他節點在拉取所有節點上的相同 key 時,就會大大減少需要拉取的數據數量,從而也就減少了磁盤 IO 以及網絡傳輸開銷。
■ 內存調優

Flink 的內存結構剛剛我們已經提及到了,所以我們清楚,調優的方面主要是針對 非堆內存 Network buffer ,manager pool 和堆內存的調優,這些基本都是通過參數來進行控制的。
這些參數我們都需要結合自身的情況去進行調整,這裏只給出一些建議。而且對於 ManagerBuffer 來說,Flink 的流式作業現在並沒有過多使用到這部分的內存,所以我們都會設置得比較小,不超過0.3。

堆內存的調優是關於 JVM 方面的,主要就是將默認使用的垃圾回收器改爲 G1 ,因爲默認使用的 Parallel Scavenge 對於老年代的 GC 存在一個串行化的問題,它的 Full GC 耗時較長,下面是關於 G1 的一些介紹,網上資料也非常多,這裏就不展開說明了。



ClickHouse存儲計算分離在騰訊雲的實踐

Spark Streaming + Canal + Kafka打造Mysql增量數據實時進行監測分析

impala + kudu | 大數據實時計算踩坑優化指南


歡迎點贊+收藏+轉發朋友圈素質三連

文章不錯?點個【在看】吧! 

本文分享自微信公衆號 - 大數據技術與架構(import_bigdata)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章