Flink面試題梳理

公衆號:小晨說數據

微信:weixin605405145

基礎
1. Flink最小計算單位是什麼?--slot

  • 當一個應用被提交時,Dispatcher分發器就會啓動並將應用移交給一個JobManager。

  • JobManager控制一個應用程序執行的主進程,每個應用程序都會被一個不同的JobManager所控制;JobManager先接收到要執行的應用程序(包括作業圖JobGraph、邏輯數據流圖Logical dataflow gragh、打包的所有的類、庫和其他資源的jar包;JobManager會將JobGraph轉換爲一個物理層面的數據流圖(執行圖:ExecutionGragh),包含了所有可以併發執行的任務; JobManager會向資源管理器(ResourceManager)請求執行任務必要的資源,即TaskManager上的插槽(Slot),一旦獲取到足夠的資源,就會將執行圖分發到真正運行它們的TaskManager上;在運行過程中,JobManager會負責所有需要中央協調的操作,比如檢查點(checkpoints)的協調。

  • ResourceManager主要負責管理任務管理器(TaskManager)的插槽(slot),Slot時Flink定義的處理資源單元;ResourceManager將有空閒插槽的TaskManager分配給JobManager。如果ResourceManager沒有足夠的插槽來滿足JobManager的請求,它可以向資源提供平臺發起會話,以提供啓動TaskManager進程的容器。

  • TaskManager一般在Flink的工作進程中會有多個,每個TaskManager都包含一定數量的插槽slots,插槽的數量限制了TaskManager能夠執行的任務數量。TaskManager啓動之後,TaskManager會向ResoureManger註冊它的插槽,收到ResourceManger的指令後,TaskManager就會將一個或多個插槽提供個JobManager調用,JobManager就可以向插槽分配任務(tasks)來執行。在執行過程中,一個TaskManager可以跟其他運行同一應用程序的TaskManager交換數據。

2. Flink時間類型有那些,他們有什麼區別?

  •  事件時間(Event Time)事件時間是每個獨立事件在產生它的設備上發生的時間,這個時間在事件進入Flink之前就已經嵌入到事件中,時間順序取決於事件產生的地方,和下游數據處理系統的時間無關。
  • 接入時間(Ingestion Time)接入時間是數據進入Flink系統的時間,接入時間依賴Source Operator 所在主機的系統時鐘。因爲接入時間在數據接入過程生成後,時間戳不在發生變化,和後續處理數據的Operator所在機器的時鐘沒有關係,所以不會因爲某臺機器時鐘不同步或網絡延遲而導致計算結果不準確的問題。相比於Event Time,Ingestion Time 不能處理亂序事件,因此不用生成對應的Watermarks.
  • 處理時間(Processing Time)處理時間是指數據在操作算子計算過程中獲取到的所在主機。當用戶選擇使用Processing Time 時,所有和時間相關的計算算子,例如Windows計算,在當前的任務中所有算子將直接使用其所在主機的系統時間。基於Processing Time 時間概念,Flink 的程序性能相對較高,延遲也比較低,對接入到系統中的數據時間相關的計算完全交給算子內部決定。雖然性能和易用性上有優勢,但在處理數據亂序時,Processing Time 不是最優的選擇,數據本身不亂序,如果每臺機器本身的時鐘不同步也會導致數據處理過程中出現數據亂序,Processing Time 適用於時間計算精度不是特別高的計算場景。


3.Flink窗口類型有哪些,你們目前用的什麼窗口?

Tumbling window 固定相同間隔分配窗口,每個窗口之間沒有重疊。

//tumbling time windows(翻滾時間窗口)
data.keyBy(1)
    .timeWindow(Time.minutes(1)) //tumbling time window 每分鐘統計一次數量和
    .sum(1);

--------------------------------------------------------------------------------------------------

Sliding Windows 固定相同間隔分配窗口,只不過每個窗口之間有重疊。窗口重疊的部分如果比窗口小,窗口將會有多個重疊,即一個元素可能被分配到多個窗口裏去。

data.keyBy(1)
    .timeWindow(Time.minutes(1), Time.seconds(30)) //sliding time window 每隔 30s 統計過去一分鐘的數量和
    .sum(1);

--------------------------------------------------------------------------------------------------

Session Windows: 主要是根據活動的事件進行窗口化,他們通常不重疊,也沒有一個固定的開始和結束時間。一個session window關閉通常是由於一段時間沒有收到元素。在這種用戶交互事件流中,我們首先想到的是將事件聚合到會話窗口中(一段用戶持續活躍的週期),由非活躍的間隙分隔開。

// 靜態間隔時間
WindowedStream<MovieRate, Integer, TimeWindow> Rates = rates
                .keyBy(MovieRate::getUserId)
                .window(EventTimeSessionWindows.withGap(Time.milliseconds(10)));
// 動態時間
WindowedStream<MovieRate, Integer, TimeWindow> Rates = rates
                .keyBy(MovieRate::getUserId)
                .window(EventTimeSessionWindows.withDynamicGap(()));

--------------------------------------------------------------------------------------------------

Global window:同keyed的元素分配到一個窗口裏

WindowedStream<MovieRate, Integer, GlobalWindow> Rates = rates
    .keyBy(MovieRate::getUserId)
    .window(GlobalWindows.create());

 

4.Flink的狀態你們有沒有用過,用的什麼類型的狀態?

 

  1. flink state 分類,帶key的,和不帶key的
    1. KeyedState
    2. OperatorState
  2. flink state類型,一半比較常用的 ValueStateListState,MapState
    1. ValueState<T>: This keeps a value that can be updated and retrieved (scoped to key of the input element as mentioned above, so there will possibly be one value for each key that the operation sees). The value can be set using update(T) and retrieved using T value().

    2. ListState<T>: This keeps a list of elements. You can append elements and retrieve an Iterable over all currently stored elements. Elements are added using add(T) or addAll(List<T>), the Iterable can be retrieved using Iterable<T> get(). You can also override the existing list with update(List<T>)

    3. ReducingState<T>: This keeps a single value that represents the aggregation of all values added to the state. The interface is similar to ListState but elements added using add(T) are reduced to an aggregate using a specified ReduceFunction.

    4. AggregatingState<IN, OUT>: This keeps a single value that represents the aggregation of all values added to the state. Contrary to ReducingState, the aggregate type may be different from the type of elements that are added to the state. The interface is the same as for ListState but elements added using add(IN) are aggregated using a specified AggregateFunction.

    5. FoldingState<T, ACC>: This keeps a single value that represents the aggregation of all values added to the state. Contrary to ReducingState, the aggregate type may be different from the type of elements that are added to the state. The interface is similar to ListState but elements added using add(T) are folded into an aggregate using a specified FoldFunction.

    6. MapState<UK, UV>: This keeps a list of mappings. You can put key-value pairs into the state and retrieve an Iterable over all currently stored mappings. Mappings are added using put(UK, UV) or putAll(Map<UK, UV>). The value associated with a user key can be retrieved using get(UK). The iterable views for mappings, keys and values can be retrieved using entries()keys() and values() respectively. You can also use isEmpty() to check whether this map contains any key-value mappings.

  3. flink的state可以設置過期時間,類似於redis

import org.apache.flink.api.common.state.StateTtlConfig;
StateTtlConfig ttlConfig = StateTtlConfig
    .newBuilder(Time.seconds(1))
    .disableCleanupInBackground()
    .build();

5.Flink如何處理延遲數據?

Flink的窗口處理流式數據雖然提供了基礎EventTime的WaterMark機制,但是隻能在一定程度上解決數據亂序問題。而某些極端情況下數據延遲會非常嚴重,即便通過WaterMark機制也無法等到數據全部進入窗口再進行處理。默認情況下,Flink會將這些嚴重遲到的數據丟棄掉;如果用戶希望即使數據延遲到達,也能夠按照流程處理並輸出結果,此時可以藉助Allowed Lateness機制來對遲到的數據進行額外的處理。

  • 通過watermark機制來處理out-of-order的問題,屬於第一層防護,屬於全局性的防護,通常說的亂序問題的解決辦法,就是指這類;
  • 通過窗口上的allowedLateness機制來處理out-of-order的問題,屬於第二層防護,屬於特定window operator的防護,late element的問題就是指這類。
  • flink採用watermark allowedLateness() sideOutputLateData()三個機制來保證獲取數據
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.assigners.SlidingEventTimeWindows
import org.apache.flink.streaming.api.windowing.time.Time
 
object demo1 {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime )
    val inputStream: DataStream[String] = env.socketTextStream("hadoop102",7777)
    val outputTag = new OutputTag[SensorReading]("side")
    val dataStream = inputStream
      .map(data => {
        val dataArray = data.split(",")
        SensorReading(dataArray(0).trim, dataArray(1).trim.toLong, dataArray(2).trim.toDouble)
      }).assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[SensorReading](Time.seconds(2)) {
      override def extractTimestamp(element: SensorReading): Long = {
        element.timestamp*1000 //我的測試時間戳是s,flink要求ms
      }
    })
    val minStream: DataStream[SensorReading] = dataStream.keyBy(_.id)
      //  .window( SlidingEventTimeWindows.of(Time.seconds(10),Time.seconds(2)))
       .timeWindow(Time.seconds(10))
      .allowedLateness(Time.seconds(4))
      .sideOutputLateData(outputTag)
      .minBy("temperature")
    dataStream.print("data")
    minStream.print("min")
    minStream.getSideOutput(outputTag).print("slide")
    env.execute("demo1")
  }
}
case class SensorReading(id: String, timestamp: Long, temperature: Double)

注意參數

1、窗口開窗爲10s ,此次採用滾動窗口比較簡單點

2、watermark爲2s

3、允許延遲爲4s

注意事項

1、如果用我的代碼進行測試,不要修改測試數據第一條,因爲涉及到計算窗口的start

測試數據

sensor_1, 1547718120,20
sensor_1, 1547718130,10
sensor_1, 1547718131,9
sensor_1, 1547718132,8
sensor_1, 1547718120,9
sensor_1, 1547718135,5
sensor_1, 1547718120,9
sensor_1, 1547718136,4
sensor_1, 1547718120,9

打印結果

data> SensorReading(sensor_1,1547718120,20.0)
data> SensorReading(sensor_1,1547718130,10.0)
data> SensorReading(sensor_1,1547718131,9.0)
data> SensorReading(sensor_1,1547718132,8.0)  
min> SensorReading(sensor_1,1547718120,20.0)
data> SensorReading(sensor_1,1547718120,9.0)
min> SensorReading(sensor_1,1547718120,9.0)
data> SensorReading(sensor_1,1547718135,5.0)
data> SensorReading(sensor_1,1547718120,9.0)
min> SensorReading(sensor_1,1547718120,9.0)
data> SensorReading(sensor_1,1547718136,4.0)
data> SensorReading(sensor_1,1547718120,9.0)
slide> SensorReading(sensor_1,1547718120,9.0)

說明

1、經過計算窗口的開始時間是1547718120,所以第一個窗口是【20-30),

2、第一個窗口關閉的時間是20+10+2=32,所以當輸入32這條數據的時候【20-30)的窗口關閉,此時窗口內的數據只有20,所以算出溫度最小值爲20

3、當輸入 SensorReading(sensor_1,1547718120,9.0)這條數據的時候,allowlateness起作用,認爲這條數據也是延遲數據,對原先算出的最小值20進行修正,最後算出min=9.0

4、此時需要計算一個最多延遲時間20+10+2+4=36,所以輸入35的時候,這條數據,會進入到第二個窗口,同時第一個窗口還沒有徹底關閉,所以再次輸入 SensorReading(sensor_1,1547718120,9.0),仍然會進入到【20-30)的窗口,並在此計算最小值

5、輸入SensorReading(sensor_1,1547718136,4.0),窗口徹底關閉,再次輸入 SensorReading(sensor_1,1547718120,9.0),不再對第一個窗口min進行修正,直接把數據放到測輸入流,以後所有的【20-30)的數據在輸入都會全部放到側輸出流

總結

1、窗口window 的作用是爲了週期性的獲取數據

2、watermark的作用是防止數據出現亂序(經常),事件時間內獲取不到指定的全部數據,而做的一種保險方法,

3、allowLateNess,是將窗口關閉時間再延遲一段時間,

   思考?這裏的allowLateNess 感覺就好像window變大了,那麼爲什麼不直接把window設置大一點呢?或者把watermark加大點

    業務需要,比如我業務需要統計每個小時內的數據,那麼開窗一定是1h,但是數據亂序可能會達到幾分鐘,一般來說水印設置的都比較小(爲什麼呢?暫時不知道),所以提出了延遲時間這個概念

4、sideOutPut是最後兜底操作,所有過期延遲數據,指定窗口已經徹底關閉了,就會把數據放到側輸出流

6.Flink中數據異常如何修復

  1. 涉及到state,可以通過Queryable State來查詢和修改state
  2. 通過制定歷史時間戳回滾歷史數據,(這個地方需要注意的細節很多,你回滾歷史數據,你之前寫入的數據如何處理,是刪除還是可以覆蓋寫入,自己思考清楚)
  3. 任務異常掛掉,可以通過checkpoint啓動任務

7.流處理和批處理的區別,流處理的難點在哪裏?

在流處理中保證高性能同時又要保證容錯是比較困難的。在批處理中,當作業失敗時,可以容易地重新運行作業的失敗部分來重新計算丟失的結果。這在批處理中是可行的,因爲文件可以從頭到尾重放。但是在流處理中卻不能這樣處理。數據流是無窮無盡的,沒有開始點和結束點。帶有緩衝的數據流可以進行重放一小段數據,但從最開始重放數據流是不切實際的(流處理作業可能已經運行了數月)。此外,與僅具有輸入和輸出的批處理作業相比,流計算是有狀態的。這意味着除了輸出之外,系統還需要備份和恢復算子狀態。由於這個問題比較複雜,因此在開源生態系統中有許多容錯方法去嘗試解決這個問題。

用於容錯機制對整個框架的架構有比較深的影響。很難將不同的容錯機制進行插件化來整合到現有框架中。因此,在我們選擇一個流處理框架時,容錯機制也非常重要。

下面我們去了解一下流處理架構的幾種容錯方法,從記錄確認微批處理事務更新分佈式快照。我們將從以下幾個維度討論不同方法的優缺點,最終選出融合不同方法優點適合流處理程序的融合方法:

  • Exactly-once語義保證:故障後有狀態算子的狀態能正確恢復。
  • 低延遲:延遲越低越好。許多應用程序需要亞秒級延遲。
  • 高吞吐量:隨着數據速率的增長,通過管道推送大量數據至關重要。
  • 強大的計算模型:框架應該提供一種編程模型,該模型不會對用戶進行限制並保證應用程序在沒有故障的情況下容錯機制的低開銷。
  • 流量控制:處理速度慢的算子產生的背壓應該由系統和數據源自然吸收,以避免因消費緩慢而導致崩潰或性能降低。

上面我們忽略了一個共同特徵,即失敗後的快速恢復,不是因爲它不重要,而是因爲(1)所有介紹的系統都能夠基於完全並行進行恢復,以及(2)在有狀態的應用程序中,狀態恢復的瓶頸通常在於存儲而不是計算框架。


8.Flink整個checkpoint流程說一下,誰生成的barrier,checkpoint的信息誰記錄?

下面這個圖非常清晰的解釋了。


9.Flink自定義sink和source有沒有寫過,遇到了什麼問題?
10.Flink自定udf函數有沒有寫過,解決的什麼問題?

10.Flink爲什麼吞吐大,他在架構上有什麼特別之處相較於storm和spark

  1. 爲什麼storm吞吐小
    1. Storm使用上游備份和記錄確認機制來保證在失敗後重新處理消息。
    2. 記錄確認機制的工作方式如下:算子(Operator)處理的每條記錄都會向前一個算子發回一個已經處理過的確認。拓撲的 Source 節點會保留它產生的所有元組的一個備份。直到 Source 中記錄收到其所產生的到Sink的所有派生記錄的確認之後,就可以刪除上游備份的備份。當發生故障時,如果沒有收到所有的確認,Source 記錄就會重新發送。這種機制可以保證不會丟失數據,但很有可能導致重複處理記錄(我們稱之爲At-Least-Once語義)
    3. 純記錄確認體系結構,無論其性能如何,都無法提供Exactly-once語義保證,這給應用程序開發人員帶來了刪除重複數據的負擔。對於某些應用程序而言,這可能是可以接受的,但對於其他應用程可能並不能接受。Storm的機制的其他問題還有吞吐量低和流量控制的問題,在出現背壓的情況下,記錄確認機制會導致上游節點錯誤地認爲數據處理出現了故障(實際上僅僅是由於出現背壓導致記錄來不及處理,而無法發送確認)。這導致了基於微批處理的流式架構的發展。

項目
1.你們項目中有沒有遇到過背壓?如何解決的?
2.你們項目中有沒有遇到數據傾斜?如何解決的?
3.你們項目中有沒有遇到狀態異常需要人工修改?如何解決的?
4.你們項目中有沒有遇到離線數據歷史數據需要遷移到實時流中,比如歷史視頻的播放量,想要銜接到實時流中進行累加?如何解決的?
5.你們項目中有沒有遇到手動維護kafka的offset,如何獲取kafka的offset?
6.你們項目中有沒有遇到checkpoint的oom現象,rocksDB有點和不足,checkpoint和savepoint的區別是什麼?
7.你們項目中有沒有遇到異步io讀寫的場景?
8.你們項目中有沒有使用過廣播的場景?
9.你們項目中有沒有使用實時去重複,實時topN的場景,如何做的?

面試
1.梳理項目背景,你做的什麼項目,數據量多少,這個項目應用場景。
2.每天多少條數據,數據量多大容量(多少TB)每秒鐘處理多少條數據,你在項目中遇到了哪些問題,你是如何解決的?
3.項目中你用到了什麼技術,這個技術有什麼優點和不足,你要思考,爲什麼選這個技術,其他技術爲什麼可以?這個你要思考。
4.你的任務什麼時間調度,有沒有相應的監控,數據異常了有沒有報警
5.思考好項目組分工,如何跟前端交互的,數據來源+加工+呈現,這個流程梳理清楚

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