Flink相比传统的Spark Streaming区别?

关键点:Flink是标准的实时处理引擎,基于事件驱动。而Spark Streaming是微批模型。
架构上
SparkStream运行时的角色有:Master、Worker、Driver、Executor。
Flink在运行时主要包括:Jobmanager、Taskmanager和Slot。
任务调度上
Spark Streaming连续不断的生成微小的数据批次,构建有向无环图DAG,并依次创建DStreamGraph、JobGenerator、JobScheduler。
Flink将代码转换为StreamGraph,经过优化生成JobGraph,然后提交给JobManager进行处理,jobManager会根据JobGraph生成ExecutionGraph,ExecutionGraph是Flink调度最核心的数据结构,JobManager根据ExecutionGraph对Job进行调度。
时间机制
Spark Streaming支持的时间机制有限,只支持处理时间。Struct Streaming支持事件时间。
Flink支持处理时间,事件时间,注入时间。同时也支持watermark机制来处理滞后数据。
容错机制
SparkStreaming可以设置checkpoint,发生故障可从checkpoint处重启恢复,但是这个行为只能使得数据不丢失,可能会重复处理,不能做到精确一次的处理语义。Flink则使用两阶段提交协议来解决了这个问题。

Flink的运行必须依赖Hadoop组件吗?

Flink理论上可以完全独立于Hadoop组件,在不依赖Hadoop组件下运行。但是在hadoop的生态环境下,很多的大数据组件是绕不过去的,比如Flink集成了HDFS、Yarn、Hbase。例如:Flink可以和Yarn集成做资源调度,也可以读写HDFS,或者利用HDFS做检查点。

Flink的并行度了解吗?Flink的并行度设置是怎么样的?

Flink的并行度体现在Task可以分为多个SubTask,其中每个SubTask处理其中的一部分数据。这些并行实例的数量被称为并行度。
Flink可以从四个层面设置并行度:

  • 操作算子层面
  • 执行环境层面
  • 客户端层面
  • 系统层面

优先级依次下降

说说Flink中的广播变量,使用时需要注意什么?

Flink中的广播变量的概念可以参考Spark中的广播变量的概念。
在Flink中的Standlone模式下,广播变量存放在节点TaskManager,其中Slot共享广播变量中数据。
使用Broadcast state需要注意

  • 没有跨任务的通信,这就是为什么只有广播可以修改Broadcast state的原因。
  • 用户必须保证以相同的方式更新Broadcast state,否则结果可能不同
  • broadcast state 无法保证元素的到达顺序,因此不能依赖传入事件的顺序
  • 所有任务都会把Broadcast state存入checkpoint,虽然checkpoint发生时所有任务都具有相同的Broadcast state。这是为了避免恢复期间所有任务从同一文件中进行恢复,然而代价是state在checkpoint时的大小成倍数增加。
  • Flink确保在恢复或改变并行度时不会有重复数据,也不会丢失数据。在具有相同或者改小并行度后恢复的情况下,每个任务读取其状态checkpoint。在并行度增加时,原先的每个任务都会读取自己的状态,新增的任务以循环的方式读取前面任务的检查点,不支持RockDB state backend,Broadcast state在运行时保存在内存中。

说说Flink中的状态存储?

Flink在做计算的过程中需要存储中间状态,来避免数据的丢失和状态恢复。选择的状态存储策略不同,会影响状态持久化如何和checkpoint交互。Flink提供了三种状态存储方式:MemoryStateBackend、FsStateBackend、RocksDBStateBackend。

Flink中的时间有哪几类

process Time: 处理时间,Flink算子处理事件的时间
Ingesting Time: 摄入时间,事件进入Source时,系统的时间
Event Time: 事件时间,要求消息本身就带有时间戳,作为事件发生的时间

Flink是如何做到高效的数据交换的?

在一个Flink job中,数据需要在不同的task中进行交换,整个数据交换是有TaskManager负责的。
TaskManager的网络组件先从buffer中收集records,然后再发送。Records并不是一个一个被发送的,而是积累一个批次再发送,batch技术可以更加高效的利用网络资源。

基本上所有的大数据组件想做到大吞吐量都是通过batch来实现的。
例如kafka、sparkstream、strom(Trident)等

Flink分布式快照的原理是什么?

Flink的分布式快照是根据Chandy-Lamport算法量身定做的。简单来说就是持续创建分布式数据流及其状态的一致快照。
核心思想就是:在input source端插入barrier(数据栅栏), 控制barrier的同步来实现snapshot的备份和exactly-once语义。
Flink 分布式快照的核心元素之一是 Barrier(数据栅栏),我们也可以把 Barrier 简单地理解成一个标记,该标记是严格有序的,并且随着数据流往下流动。每个 Barrier 都带有自己的 ID,Barrier 极其轻量,并不会干扰正常的数据处理。
image.png
如上图所示,假如我们有一个从左向右流动的数据流,Flink 会依次生成 snapshot 1、 snapshot 2、snapshot 3……Flink 中有一个专门的“协调者”负责收集每个 snapshot 的位置信息,这个“协调者”也是高可用的。
Barrier 会随着正常数据继续往下流动,每当遇到一个算子,算子会插入一个标识,这个标识的插入时间是上游所有的输入流都接收到 snapshot n。与此同时,当我们的 sink 算子接收到所有上游流发送的 Barrier 时,那么就表明这一批数据处理完毕,Flink 会向“协调者”发送确认消息,表明当前的 snapshot n 完成了。当所有的 sink 算子都确认这批数据成功处理后,那么本次的 snapshot 被标识为完成。
这里就会有一个问题,因为 Flink 运行在分布式环境中,一个 operator 的上游会有很多流,每个流的 barrier n 到达的时间不一致怎么办?这里 Flink 采取的措施是:快流等慢流。
image.png

拿上图的 barrier n 来说,其中一个流到的早,其他的流到的比较晚。当第一个 barrier n到来后,当前的 operator 会继续等待其他流的 barrier n。直到所有的barrier n 到来后,operator 才会把所有的数据向下发送。

异步和增量
按照上面我们介绍的机制,每次在把快照存储到我们的状态后端时,如果是同步进行就会阻塞正常任务,从而引入延迟。因此 Flink 在做快照存储时,可采用异步方式
此外,由于 checkpoint 是一个全局状态,用户保存的状态可能非常大,多数达 G 或者 T 级别。在这种情况下,checkpoint 的创建会非常慢,而且执行时占用的资源也比较多,因此 Flink 提出了增量快照的概念。也就是说,每次都是进行的全量 checkpoint,是基于上次进行更新的。

Flink任务延迟高,想解决这个问题,你会如何入手?

在Flink的后台任务管理中,我们可以看到Flink的哪个算子和task出现了反压。最主要的手段是资源调优和算子调优。
资源调优即是对作业中的Operator的并发数(parallelism)、CPU(core)、堆内存(heap_memory)等参数进行调优。
作业参数调优包括:并行度的设置,State的设置,checkpoint的设置。

Flink是如何处理反压的?

Flink内部是基于producer-consumer模型来进行消息传递的,Flink的反压设计也是基于整个模型。Flink使用了高效有界的分布式阻塞队列,就像Java通用的阻塞队列(BlockingQueue)一样。下游消费者消费变慢,上游就会受到阻塞。
其他实时流框架处理反压机制:
Storm:Storm会主动监控工作节点,当工作节点接受数据超过了一定的水位值时,那么反压信息就会发到zookeeper上,然后zookeeper通知所有的工作节点进入反压状态,最后数据的生产源头就会降低数据的发送速度。
Spark Streaming:在原有的架构基础上专门设计了一个RateController(速率控制)组件,该组件利用了经典的PID算法。向系统反馈当前系统处理数据的几个重要属性:消息数量、调度时间、处理时间等,通过这些参数计算出一个速率,该速率则是当前系统处理数据的最大能力,Spark Streaming会根据计算结果对生产者进行限速。
Flink:四个字:“逐级反压”,算子之间的关系犹如生产者和消费者之间的关系,在消费者处理数据能力跟不上生产者的时候,消费者的分布式阻塞队列(Receive Buffer)会呈现队列满的情况,于是上级算子发送数据(Send Buffer)就会感知到这一情况,就会将发送数据的速率降低。

Operator Chains(算子链)这个概念你了解吗?

为了更高效地分布式执行,Flink会尽可能地将operator的subtask链接(chain)在一起形成task。每个task在一个线程中执行。
将operators链接成task是非常有效的优化:它能减少线程之间的切换,减少消息的序列化/反序列化,减少数据在缓冲区的交换,减少了延迟的同时提高整体的吞吐量。

Flink什么情况下才会把Operator chain在一起形成算子链?

两个operator chain在一起的条件:

  1. 上下游的并行度一致
  2. 下游节点的入度为1(也就是说下游节点没有来自其他节点的输入)
  3. 上下游节点都在同一个slot group 中
  4. 下游节点的chain策略为Always(可以与上下游链接,map、flatmap、filter等默认都是ALWAYS)
  5. 上游节点的chain策略为ALWAYS或HEAD(只能与下游链接,不能与上游链接,Source默认是HEAD)
  6. 两个节点间数据分区方式是forward
  7. 用户没有禁用chain