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.思考好项目组分工,如何跟前端交互的,数据来源+加工+呈现,这个流程梳理清楚

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