背景
- 身在大数据工程团队,经常同ODPS&D2&Flink打交道,了解掌握大数据相关知识对工程端还是有必要;早期团队技术之美围绕Flink专题学习了论文《Apache Flink™: Stream and Batch Processing in a Single Engine》,此后在两年的时间断断续续里消化理解该论文;通过网上资料进行了更深入的了解,这里将学习到的内容记录下来。
什么是大数据工程
说起大数据工程,大数据到底是从什么时候火起来的?这个问题并没有一个确切的答案,如果一定要说,2010 年算是初现苗头。但是,作为大数据时代开拓者的谷歌,它的“三架马车”远远早于 2010 年诞生:谷歌文件系统第一次公开发表的论文是在 2003 年,MapReduce 公开发表的时间是 2004 年,而 BigTable 则公开发表于 2006 年
Google的三篇论文一直是大数据时代的经典代表,经过十几年的演进进化,根据三篇论文有了我们日常工作中经常接触的大数据存储知识。
- 根据MapReduce,于是我们有了Hadoop;
- 根据BigTable,于是我们有了HBase。
- 根据GFS,于是我们有了HDFS;
在大数据打交道的业务场景中,除了TP场景的关系型数据MySQL外,更多的是AP场景的海量大数据的处理和计算
-
NoSQL:Hbase&Lindorm是典型代表;面向列式存储的稀疏矩阵结构,海量大数据点查场景首选存储选型,能支持千万级高并发查询,读写性能毫秒级别
-
分析型数据库:基于Lindorm数据分析能力局限性,业界衍生出一批AP场景的OLAP在线分析性数据库,基于MPP架构的火山模型和向量化的执行引擎,能较好的满足分析师对海量数据在线OLAP分析,阿里目前常用主打选型集团Hologres,号称能做到
-
点查和Hbase一样快
-
复杂OLAP查询比clickHouse、presto、impala快
- 计算引擎:计算引擎的时代,从Hadoop离线计算到Flink实时流的计算,数据产出从T+1天离线天级别到实时秒级别,具有划时代的意义,由此展开了本篇学习的Flink计算引擎
Flink前世今生
Apache三代计算引擎
讲FLink之前,对三代计算引擎简单进行个对比分析
Hadoop
-
架构:MapReduce分布式计算 + HDFS分布式文件
-
并发行高:能同时运行很多任务
-
基于Map + Reduce过程会有多次落盘,数据产出时效性不高,只能用于离线数据产出
-
底层API编程,早期对只会SQL的分析师不友好
-
当时使用Hadoop进行数据分析的大部分是数据科学家和分析师,其中分析师占更多数。分析师更习惯借助各种BI工具,使用SQL来进行自助分析,编程并不是他们技能包的一部分,因此他们对SQL on Hadoop的呼声一直很高,于是有了Hive第一款产品,集团离线D2上ODPS类似
Spark
详细可参考:Spark—RDD架构浅析
- 运算快:弹性分布式数据集RDD + 内存存储,所以需要搭配存储(常用HDFS)
- 基于checkpoint的备份机制,容错机制比Hadoop方式科学,恢复数据不用重新计算
- 伪实时,使用微批式处理实时计算(有界数据流),延时较高(0.5~2s)
FLink
基于Spark处理实时的延时问题,apache Flink横空出时:基于事件驱动, 面向流处理架构
- 真正流式计算,无界数据流
- 事件驱动方式,基于event + window 窗口计算方式
- 基于checkpoint + state机制,不用在STW的情况下快速恢复failover job
- 唯一缺陷:流批一体局限性(apache paimon 致力解决离线实时技术壁垒)
Flink简介
Flink VS Spark
Flink和Spark二者的区别在于无限数据流的数据会随时间的推演而持续增加,计算持续进行且不存在结束的状态,相对的有限数据流数据大小固定,计算最终会完成并处于结束的状态。
在 Spark 的世界观中,一切都是由批次组成的,离线数据是一个大批次,而实时数据是由一个一个无限的小批次组成的。 --流是批的特例
而在 Flink的世界观中,一切都是由流组成的,离线数据是有界限的流,实时数据是一个没有界限的流,这就是所谓的有界流和无界流。 --批是流的特例
Flink常用场景
Flink 常见的三种场景:事件驱动应用、实时ETL和数据流、实时数据分析
事件驱动应用
在触发某些规则后,Data Driven 会进行处理或者是进行预警,这些预警会发到下游产生业务通知,这是Data Driven 的应用场景,Data Driven 在应用上更多应用于复杂事件的处理。
实时推荐(例如在客户浏览商家页面的同时进行商品推荐)
模式识别或复杂事件处理(例如根据信用卡交易记录进行欺诈识别)
异常检测(例如计算机网络入侵检测)
实时数据分析
- 移动应用中的用户行为分析
- 消费者技术中的实时数据即席查询
实时ETL和数据流
- 实时数仓
当下游要构建实时数仓时,上游则可能需要实时的Stream ETL。这个过程会进行实时清洗或扩展数据,清洗完成后写入到下游的实时数仓的整个链路中,可保证数据查询的时效性,形成实时数据采集、实时数据处理以及下游的实时Query。
- 搜索引擎推荐
搜索引擎这块以淘宝为例,当卖家上线新商品时,后台会实时产生消息流,该消息流经过Flink 系统时会进行数据的处理、扩展。然后将处理及扩展后的数据生成实时索引,写入到搜索引擎中。这样当淘宝卖家上线新商品时,能在秒级或者分钟级实现搜索引擎的搜索。
Flink架构
相关概念
讲Flink架构前,介绍几个Flink内部概念,一个完整的Blink任务主要有三部分组成:
- 数据源(INPUT) - Flink外部数据源表 --Source
- 业务逻辑(QUERY) - Flink内部处理逻辑 --Operator
- 结果表(OUTPUT),如下:Flink外部结果表 --Sink
一个Flink Job先是由外部输入经过Source算子转换为Source,然后在数据流中经过一系列的算子运算,再由Sink算子转换为输出流,流入外部结果表。
集群启动架构
当 Flink集群启动后,首先会启动一个 JobManger和一个或多个的 TaskManager。由 Client 提交任务给 JobManager,JobManager再调度任务到各个 TaskManager去执行,然后 TaskManager将心跳和统计信息汇报给 JobManager。TaskManager之间以流的形式进行数据的传输。上述三者均为独立的 JVM 进程。
•Client 为提交 Job 的客户端,可以是运行在任何机器上(与 JobManager环境连通即可)。提交 Job 后,Client 可以结束进程(Streaming的任务),也可以不结束并等待结果返回。
•JobManager 主要负责调度 Job 并协调 Task 做 checkpoint,职责上很像 Storm 的 Nimbus。从 Client 处接收到 Job 和 JAR 包等资源后,会生成优化后的执行计划,并以 Task 的单元调度到各个 TaskManager去执行。
•TaskManager 在启动的时候就设置好了槽位数(Slot),每个 slot 能启动一个 Task,Task 为线程。从 JobManager处接收需要部署的 Task,部署启动后,与自己的上游建立 Netty连接,接收数据并处理。
软件架构
API & Libraries 层
这一层主要提供了编程 API 和 顶层类库:
编程 API : 用于进行流处理的 DataStream API 和用于进行批处理的 DataSet API;
顶层类库:包括用于复杂事件处理的 CEP 库;用于结构化数据查询的 SQL & Table 库,以及基于批处理的机器学习库 FlinkML和 图形处理库 Gelly。
Runtime 核心层
这一层是 Flink分布式计算框架的核心实现层,包括作业转换,任务调度,资源分配,任务执行等功能,基于这一层的实现,可以在流式引擎下同时运行流处理程序和批处理程序。
Runtime的输入是物理执行计划,输出是最终结果。涉及代码生成、向量化等技术
物理部署层
Flink的物理部署层,用于支持在不同平台上部署运行 Flink应用。
Flink执行拓扑Graph
Flink 中的执行图可以分成四层:StreamGraph -> JobGraph -> ExecutionGraph -> 物理执行图。
- **StreamGraph:**是根据用户通过 Stream API 编写的代码生成的最初的图。用来表示程序的拓扑结构。
- **JobGraph:**StreamGraph经过优化后生成了 JobGraph,提交给 JobManager 的数据结构。主要的优化为,将多个符合条件的节点 chain 在一起作为一个节点,这样可以减少数据在节点之间流动所需要的序列化/反序列化/传输消耗。
- **ExecutionGraph:**JobManager 根据 JobGraph 生成ExecutionGraph。ExecutionGraph是JobGraph的并行化版本,是调度层最核心的数据结构。
- **物理执行图:**JobManager 根据 ExecutionGraph 对 Job 进行调度后,在各个TaskManager 上部署 Task 后形成的“图”,并不是一个具体的数据结构。
- **StreamGraph:**根据用户通过 Stream API 编写的代码生成的最初的图。
- StreamNode:用来代表 operator 的类,并具有所有相关的属性,如并发度、入边和出边等。
- StreamEdge:表示连接两个StreamNode的边。
- **JobGraph:**StreamGraph经过优化后生成了 JobGraph,提交给 JobManager 的数据结构。
- JobVertex:经过优化后符合条件的多个StreamNode可能会chain在一起生成一个JobVertex,即一个JobVertex包含一个或多个operator,JobVertex的输入是JobEdge,输出是IntermediateDataSet。
- IntermediateDataSet:表示JobVertex的输出,即经过operator处理产生的数据集。producer是JobVertex,consumer是JobEdge。
- JobEdge:代表了job graph中的一条数据传输通道。source 是 IntermediateDataSet,target 是 JobVertex。即数据通过JobEdge由IntermediateDataSet传递给目标JobVertex。
- **ExecutionGraph:**JobManager 根据 JobGraph 生成ExecutionGraph。ExecutionGraph是JobGraph的并行化版本,是调度层最核心的数据结构。
- ExecutionJobVertex:和JobGraph中的JobVertex一一对应。每一个ExecutionJobVertex都有和并发度一样多的 ExecutionVertex。
- ExecutionVertex:表示ExecutionJobVertex的其中一个并发子任务,输入是ExecutionEdge,输出是IntermediateResultPartition。
- IntermediateResult:和JobGraph中的IntermediateDataSet一一对应。一个IntermediateResult包含多个IntermediateResultPartition,其个数等于该operator的并发度。
- IntermediateResultPartition:表示ExecutionVertex的一个输出分区,producer是ExecutionVertex,consumer是若干个ExecutionEdge。
- ExecutionEdge:表示ExecutionVertex的输入,source是IntermediateResultPartition,target是ExecutionVertex。source和target都只能是一个。
- Execution:是执行一个 ExecutionVertex 的一次尝试。当发生故障或者数据需要重算的情况下 ExecutionVertex 可能会有多个 ExecutionAttemptID。一个 Execution 通过 ExecutionAttemptID 来唯一标识。JM和TM之间关于 task 的部署和 task status 的更新都是通过 ExecutionAttemptID 来确定消息接受者。
- **物理执行图:**JobManager 根据 ExecutionGraph 对 Job 进行调度后,在各个TaskManager 上部署 Task 后形成的“图”,并不是一个具体的数据结构。
- Task:Execution被调度后在分配的 TaskManager 中启动对应的 Task。Task 包裹了具有用户执行逻辑的 operator。
- ResultPartition:代表由一个Task的生成的数据,和ExecutionGraph中的IntermediateResultPartition一一对应。
- ResultSubpartition:是ResultPartition的一个子分区。每个ResultPartition包含多个ResultSubpartition,其数目要由下游消费 Task 数和 DistributionPattern 来决定。
- InputGate:代表Task的输入封装,和JobGraph中JobEdge一一对应。每个InputGate消费了一个或多个的ResultPartition。
- InputChannel:每个InputGate会包含一个以上的InputChannel,和ExecutionGraph中的ExecutionEdge一一对应,也和ResultSubpartition一对一地相连,即一个InputChannel接收一个ResultSubpartition的输出。
为什么有这四张图,目的很简单:就是解耦,每张图各司其职,每张图对应了 Job 不同的阶段,更方便做该阶段的事情(对应软件架构图)