一個flink作業的調優
最近接手了一個flink作業,另外一個同事斷斷續續有的沒的寫了半年的,不著急,也一直沒上線,最近突然要上線,扔給我,要調通上線。
現狀是:
1.程式碼跑不動,資源給的不少,但是就是頻繁反壓。
2.checkpoint經常失敗。
3.也是最嚴重的,跑著跑著,作業就掛了。
接手之後,秉承著程式碼的業務邏輯是對的原則,開始了調優之旅,後來發現,還是要從最基本的做起,不然都是白扯。總結了如下幾條意見,供自己以後反省。
1.遵循一般的程式設計原則
程式碼到手之後,業務邏輯部分簡直不忍卒讀,整個業務邏輯在一個大函式中,多達幾百行,而且裡面很多的if語句,一個if語句就超過一屏,而且if語句大量巢狀,其他比如大量的string的累加操作等等,其實都是程式設計風格中的忌諱,因此,一邊是對同事的信任,一邊是沒勇氣看這個程式碼,剛開始從flink的機制開始調優,沒有重構程式碼,導致浪費了不少時間。後來還是鼓足勇氣重構程式碼,該拆分拆分,該合併合併,才發現中間有一個HashMap一直在Put但是沒有clear操作,MD,作業不崩潰才怪。
教訓:作業調優,先理解清楚業務,再者好好看懂程式碼,不管多亂,不然有明顯的bug都不知道,至於重構,如果時間可以,儘量吧。
2.確定最耗時的部分
因為作業的DAG圖包含6個level,首先需要確定哪個層級最耗時。因為在parse的部分頻繁反壓,也就是後面的一個level會處理跟不上。但當時說的是寫redis,以之前的理解,redis
應該很easy就能支援10W的併發寫吧,所以雖然flink提示redis sink有問題,但還是將關注點放在了parse上,也是導致了浪費時間。後來才瞭解到,雖然同事告訴我是寫redis,而且
確實也用的jedis來操作,但其實後臺是公司自研的一個類redis的庫,而且不儲存在記憶體中,磁碟的寫入速度哪裡能支援那麼大的併發,網絡卡在瞬間往往都打滿,而且寫操作還是同步阻塞的,這個也同時解釋了前兩個問題,由於寫入的併發太大,導致反壓,所以速度跟不上,速度跟不上,所以在每個level都開啟了64個併發,加上寫的同步阻塞,checkpoint coordinator需要收到將近300個task的ck 確認才能結束,往往任何一個task的緩慢,都導致了ck的timeout而失敗。
教訓:真正瞭解你的持久化儲存,瞭解他能支援的最大的併發和合適的併發及其他因素。
3.瞭解flink的機制
由於checkpoint總是失敗,而flink的checkpoint其實是基於RocksDB去做的,並且是非同步和增量的,無論如何都覺得不應該那麼容易timeout,後來開啟了flink日誌的debug級別才發現,ck雖然是非同步的,但是需要等到上游所有的barrier都到來才會觸發自己的流程,往往這個等的過程就達到分鐘級,然後每一個level往後傳遞,就會導致最後level的task在沒有
收到barrier的時候,ck以及timeout。當然,根本的原因還是反壓,導致處理的一些task處理的很慢,拖累了整個作業,畢竟barrier是和真正的資料是在一個channel中傳送的,如果
資料都積壓了,barrier當然也發不下去了。
另一個問題就是chain和slotshareing了,預設flink是開啟了chain的,整個是沒問題的,這樣其實多個operator會在一個task裡面,減少了資料傳輸的開銷,但是在調優的時候,也就
不太容易看出哪裡是效能瓶頸,slotsharing機制是進行資源分配的策略,預設都是default,所有的task會共享一個slot,也不太好找那個task是瓶頸,將不同的task的slotshareing
設定的不一樣,就可以在每個slot中是單獨的task,更容易分析是哪裡出問題了。
教訓:flink預設的機制,一般都是OK的,在找問題的時候可以改,但一般不會是問題點。
4.善用效能檢測工具
flink作業本身還是java作業,剛開始覺得跑不動的時候,將java的那些自帶工具也基本都用了一遍。
jstack,打印出執行緒棧,到底是哪裡操作在耗時。
jmap,打印出物件資訊以及dump檔案,看看具體是哪個物件在搞事。
jstat,檢視各種gc的資訊,各個記憶體區域的使用是否正常等等。
現在流行的框架,越來越多的使用堆外記憶體,而這部分記憶體的回收又不容易,發生洩漏很難查詢。
教訓:其實保持良好的編碼規範,記憶體出問題的可能不是那麼大,更多的還是使用stack看看具體是哪裡在耗時。
5.監控
flink有自己的metric,但是往往不夠,需要在作業中新增自己的metric,看起來就會很直觀。flink的debug日誌,雖然會有大量的日誌,但有些事情還是必須通過日誌來觀察。
但這次問題的解決,關鍵點還在於運維同學的報警,警示資料庫的tps太高,一番探討之後,才發現用的不是真正的redis,才找到的問題的真正所在,效能立馬提示,也沒有了反壓。
教訓:如果持久化層有監控,包括IO層,這裡往往才真正是出問題的地方,至於CPU和記憶體,反正是在yarn上,資源不夠隨時新增即可,反而是IO瓶頸,前面再使勁兒,這裡
堵住了,一切都是白扯。
這次調優,雖然最終問題解決了,但還是挺灰頭土臉的,希望以後有類似的事情,能夠有步驟有重點的推進。