02-流批OLAP 一体的 Flink 引擎介绍 | 青训营笔记
这是我参与「第四届青训营 」笔记创作活动的第1天
1-Flink概述
为什么会有流式计算的需求,为什么Flink能够脱颖而出,Flink当前的开源生态
1.1 Apache Flink的诞生背景
1.1.1 什么是大数据
大数据(
Big Data):指无法在一定时间内用常规软件工具对其进行获取、存储、管理和处理的数据集合。
大数据的 4V 特性:
- Volumes 海量化:数据规模大
- Variety 多样化:大数据的数据源和数据种类多样(结构化,半结构化…)
- Velocity 快速化:数据产生和处理的速度快
- Value 价值化:价值高但是价值密度低
1.1.2 大数据计算架构发展历史
- 史前阶段
- 传统数仓
- Oracle
- 单机(IBM大型机)
- 黑箱使用
- Hadoop
- 分布式
- Map-Reduce
- 离线计算
- Spark
- 批处理
- 流处理
- SQL高阶API
- 内存迭代计算
- Flink
- 流计算
- 实时、更快
- 流批一体
- Streaming/Batch SQL
阶段一是单机处理;单机无法处理海量数据,阶段二Google的三驾马车(GFS, MapReduce, BigTable);由于Map和Reduce的过程中数据需要落盘,处理性能较差,阶段三提出了基于内存迭代计算的方案,同时MapReduce中将所有的操作都抽象为Map和Reduce这两种操作,提供的接口比较原始,无法满足其他的需求或者实现较为复杂,Spark提供了基于SQL的高阶API,提供了更多的语义,使用更加简单。业内对于数据的实时性有了更高的要求,需要使用流式计算,并且Flink能够使用SQL统一流和批的操作。
1.1.3 为什么需要流式计算
业务背景:大数据的实时性带来的价值更大。
- 监控场景:如果能实时发现业务系统的健康状态,就能提前避免业务故障;
- 金融风控:如果实时监测出异常交易的行为,就能及时阻断风险的发生;
- 实时推荐:比如在抖音,如果可以根据用户的行为数据发掘用户的兴趣、偏好,就能向用户推荐更感兴趣的内容;
批式计算:
需要等待一批数据到齐才开始处理。
流式计算:
实时对用户产生的数据进行计算。源头的数据是源源不断的,Flink作为中间的数据处理引擎,将处理之后的数据输送给下游。
1.2 为什么Apache Flink会脱颖而出
1.2.1 流式计算引擎发展历程
Strom(Twitter) -> Spark Streaming(微批) -> MillWheel -> Kafka(Kafka Stream) -> Cloud Dataflow(Google) -> Flink -> Beam(Google)
Spark Streaming:Spark等一批数据到齐之后才能做整个的计算逻辑,Spark Streaming将一个无限流切成了一些无限小的批,每分钟的数据作为一批去调度一个比较小的Spark作业。
1.2.2 流式计算引擎对比
| Storm | Spark Streaming | Flink | |
|---|---|---|---|
| Streaming Model | Native(一条一条处理) | mini-batch | Native |
| 一致性保证 | At Least/Most Once | Exactly-Once | Exactly-Once |
| 延迟 | 低延迟(毫秒级) | 延迟较高(秒级) | 低延迟(毫秒级) |
| 吞吐 | Low | High | High |
| 容错 | ACK | RDD Based Checkpoint | Checkpoint(Chandy-Lamport) |
| StateFul | No | Yes(DStream) | Yes(Operator) |
| SQL支持 | No | Yes | Yes |
- Streaming Model:Native表示一条数据一条数据处理,实时性更高/mini-batch 微批
- 一致性保证:At Least Once(至少一次,有可能多处理,重发)/At Most Once(至多一次,数据只会往下游发一次,出故障不会重发,有可能没有被处理到)/Exactly-Once(恰好一次)
- 状态:Storm将中间计算结果存入数据库(Redis/MySQL),需要时再load回来。不用依赖外部系统保存状态,直接使用引擎提供的功能来保存状态。
1.2.3 Why Flink
Apache Flink is a framework and distributed processing engine for stateful computations over unbounded and bounded data streams.Flink has been designed to run in all common cluster environments,perform computations at in-memory speed and at any scale.
1.3 Apache Flink开源生态
Flink作为一个计算引擎,本身没有数据存储的能力,但是生态完善,可以与目前业内比较主流的存储引擎集成。
Flink的部署模式:standlone/yarn/k8s
Flink引擎之上:图计算、ML机器学习(AI Flow)、Stateful Function(状态存储?)、Flink CDC Connector(数据实时同步)
2-Flink整体架构
Flink当前的整体架构介绍,一个Flink作业如何调度和运行起来,Flink如何做到流批一体
2.1 Flink分层架构
Flink各个模块的用途
- SDK层:Flink的SDK目前主要有三类,
SQL/Table、DataStream、Python;(SQL使用方便,DataStream适用于SQL难以表达的场景,比如定时触发,pyFlink主要用于ML的场景) - 执行引擎层(Runtime层):执行引擎层提供了统一的DAG用来描述数据处理的Pipeline,不管是流还是批,都会转化为DAG图,调度层再把DAG转化成分布式环境下的Task(将Task分发到不同的worker节点去执行),Task之间通过Shuffle传输数据;
- 状态存储层:负责存储算子的状态信息;
- 资源调度层:目前Flink可以支持部署在多种环境。
2.2 Flink总体架构
Master/Slave架构、JobManager/TaskManager
一个Flink集群,主要包含以下两个核心组件:
-
JobManager(JM):负责整个任务的协调工作,包括:调度Task、触发协调Task做Checkpoint、协调容错恢复等; -
TaskManager(TM):负责执行一个DataFlowGraph的各个task以及data streams的buffer和数据交换。
具体的业务逻辑在Client端处理,Client端将生成的DAG信息交给JM,JM将DAG转换成物理可执行的图分发到Worker节点执行。
Dispatcher:接收作业,拉起JobManager来执行作业,并在JobMaster挂掉之后恢复作业;JobMaster:管理一个Job的整个生命周期,会向ResourceManager申请slot,并将task调度到对应TM上;ResourceManager:负责slot资源的管理和调度,Task manager拉起之后会向RM注册;
2.3 Flink作业示例
一个Flink作业在Flink中的处理流程、DataFlow Model设计思想
流式的 WordCount 示例,从 kafka 中读取一个实时数据流,每1Os统计一次单词出现次数,DataStream实现代码如下:
业务逻辑转换为一个Streaming DataFlow Graph
假设作业的sink算子的并发配置为1,其余算子并发为2 紧接着会将上面的Streaming DataFlow Graph转化Parallel Dataflow(内部叫Execution Graph):
map的输出会根据哈希分配到不同的keyBy节点上,而sink的并发度只有1,因此apply的结果都会到sink上。
为了更高效地分布式执行,Flink会尽可能地将不同的operator链接(chain)在一起形成Task。 这样每个Task可以在一个线程中执行,内部叫做OperatorChain,如下图的source和map算子可以Chain在一起。
source和map不需要有hash shuffle的过程,因此可以把这两个算子chain在一次,在一个线程中执行,减少线程的切换以及数据的序列化反序列化操作。在减少延迟的同时提高了整体的吞吐率。
最后将上面的Task调度到具体的TaskManager中的slot中执行,一个Slot只能运行同一个task的subTask
一个TM里面起几个Slot用户是可以自定义的,每个Slot是一个单独的线程去执行,将Task放到这个线程上,同一个TM上不同的Slot之间的资源比如CPU并没有严格地隔离,但是内存的话有一部分做了隔离。
2.4 Flink如何做到流批一体
流批一体的业务场景及挑战、Flink如何做到流批一体
2.4.1 为什么需要流批一体
举个例子:
- 在抖音中,实时统计一个短视频的播放量、点赞数,也包括抖音直播间的实时观看人数等;
- 在抖音中,按天统计创造者的一些数据信息,比如昨天的播放量有多少、评论量多少、广告收入多少;
上述架构有一些痛点:
- 人力成本比较高:批、流两套系统,相同逻辑需要开发两遍;
- 数据链路冗余:本身计算内容是一致的,由于是两套链路,相同逻辑需要运行两遍,产生一定的资源浪费;
- 数据口径不一致:两套系统、两套算子、两套UDF,通常会产生不同程度的误差,这些误差会给业务方带来非常大的困扰。
2.4.2 流批一体的挑战
流和批业务场景的特点如下表:
批式计算相比于流式计算核心的区别如下表:
2.4.3 Flink如何做到流批一体
为什么可以做到流批一体呢?
- 批式计算是流式计算的特例,Everything is Streams,有界数据集(批式数据)也是一种数据流、一种特殊的数据流;
因此,理论上我们是可以用一套引擎架构来解决上述两种场景,只不过需要对不同场景支持相应的扩展性、并允许做不同的优化策略。
流和批的shuffle service允许不一样,可插拔的。
站在Flink的角度,Everything is Streams,无边界数据集是一种数据流,一个无边界的数据流可以按时间切段成一个个有边界的数据集,所以有界数据集(批式数据)也是一种数据流。 因此,不管是有边界的数据集(批式数据)还是无边界数据集,Flink都可以天然地支持,这是Flink支持流批一体的基础。并且Flink在流批一一体上,从上面的API到底层的处理机制都是统一的,是真正意义上的流批一体。
Apache Flink主要从以下几个模块来做流批一体:
- SQL层;
- DataStream API层统一,批和流都可以使用DataStream API来开发;
- Scheduler层架构统一,支持流批场景;
- Failover Recovery层架构统一,支持流批场景;
- Shuffle Service层架构统一,流批场景选择不同的Shuffle Service;
2.4.4 流批一体的Scheduler层
Scheduler主要负责将作业的DAG转化为在分布式环境中可以执行的Task
在1.12之前的Flink版本中,Flink支持以下两种调度模式:
先处理上游,上游数据处理完之后将数据写入磁盘,可以再用上游释放的资源调度下游。
EAGER模式
- 16个task会一起调度,集群需要有足够的资源
12个task能够拿到所有资源,流作业才能调度起来。
LAZY模式
-
最小调度一个task即可,集群有1个slot资源可以运行
-
由Pipeline的数据交换方式连接的Task构成为一个Pipeline Region;
-
本质上,不管是流作业还是批作业,都是按照Pipeline Region粒度来申请资源和调度任务。
lazy模式性能最差,但是所需的资源最少。
eager模式性能最好,但是所需的资源最多。
ALL EDGES BLOCKING: 所有Task之间的数据交换都是 BLOCKING模式; 分为12个pipeline region;
数据不是实时传过去的,一个Task的执行完之后需要先将数据落盘,下一个Task从磁盘读取数据。
ALL EDGES PIPELINED: 所有Task之间的数据交换都是 PIPELINE模式; 分为1个pipeline region;
A1的数据不落盘,直接在内存中处理
2.4.5 流批一体的Shuffle Service层
Shuffle:在分布式计算中,用来连接上下游数据交互的过程叫做Shuffle。
实际上,分布式计算中所有涉及到上下游衔接的过程,都可以理解为Shuffle。
针对不同的分布式计算框架,Shuffle通常有几种不同的实现:
- 基于文件的Pull Based Shuffle,比如Spark或MR,它的特点是具有较高的容错性,适合较大规模的批处理作业,由 于是基于文件的,它的容错性和稳定性会更好一些;
- 基于Pipeline的Push Based Shuffle,比如Flink、Storm、Presto等,它的特点是低延迟和高性能,但是因为shuffle 数据没有存储下来,如果是batch任务的话,就需要进行重跑恢复;
shuffle的数据没有落盘,直接存在内存中推给下游。
流和批Shuffle之间的差异:
- Shuffle数据的生命周期:流作业的Shuffle数据与Task是绑定的(Task销毁了,数据就没了),而批作业的Shuffle数据与Task是解耦的(上游结束了,shuffle还在);
- Shuffle数据存储介质:流作业的生命周期比较短、而且流作业为了实时性,Shuffle通常存储在内存中,批作业因为数据量比较大以及容错的需求,一般会存储在磁盘里;
- Shuffle的部署方式:流作业Shuffle服务和计算节点部署在一起(pipeline),可以减少网络开销,从而减少latency,而批作业则不同。
落盘(本地)或者Remote shuffle service,将shuffle直接写入远端服务,下游直接从那里拉取数据,多副本存储可以提供更高的容错性。
Flink对于流和批提供两种类型的Shuffle,虽然Streaming和Batch Shuffle在具体的策略上存在一定的差异,但本质上都是为了对数据进行Re-Partition,因此不同的Shuffle之间是存在一定的共性的。
所以Flink的目标是提供一套统一的Shuffle架构,既可以满足不同Shuffle在策略上的定制,同时还能避免在共性需求上进行重复开发。
在Streaming和OLAP场景
- 为了性能的需要,通常会使用基于Pipeline的Shuffle模式
在Batch场景
- 一般会选取Blocking的Shuffle模式
为了统一Flink在Streaming和Batch模式下的Shuffle架构,Flink实现了一个Pluggable的ShuffleService框架,抽象出一些公共模块。
对于Shuffle Service,Flink开源社区已经支持:
- Netty Shuffle Service:既支持pipeline又支持blocking,Flink默认的shuffle Service策略;
- Remote Shuffle Service:既支持pipeline又支持blocking,不过对于pipeline模式,走remote反而会性能下降,主要是有用在batch的blocking场景,字节内部是基于CSS来实现的RSS。
经过相应的改造和优化之后,Flink在架构设计上,针对DataStream层、调度层、ShuffleService层,均完成了对流和批的支持。
至此,业务已经可以非常方便地使用Flink解决流和批场景的问题了。
3-Flink架构优化
流/批/OLAP三种业务场景概述,Flink如何来支持OLAP场景需求,需要做哪些架构上的优化
3.1 流/批/OLAP业务场景概述
在实际生产环境中,针对不同的应用场景,我们对数据处理的要求是不同的:
- 有些场景下,只需离线处理数据,对实时性要求不高,但要求系统吞吐率高,典型的应用是搜索引擎构建索引;
三种业务场景的的特点比对如下表:
3.2 为什么三种场景可以用一套引擎来解决
3.3 Flink如何支持OLAP场景
3.3.1 Flink做OLAP的优势
流、批、OLAP用一套引擎来处理
3.3.2 Flink OLAP场景的挑战
小作业(秒级),每秒都在创建新的作业,频繁启停,碎片。针对AP的场景做一些特殊的优化。
3.3.3 Flink OLAP架构现状
OLAP与流、批还是有一些差异
为了支持高并发,希望在一个集群内对JM具有高扩展性,JM有多个,ResourceManager只有一个,来支持更大的QPS
Gateway中对SQL的解析Parser与Flink的版本是强相关的,如果和Flink Session Cluster独立可能会带来问题,统一管理的成本较高
JM管理了这个计算任务的所有功能,高并发场景下性能会有影响。
频繁创建Task,耗时严重
3.3.4 Flink在OLAP架构的问题与设想
4-精选案例讲解
选择两个字节内部真实的案例场景,介绍Flink在流批一体以及OLAP上的实践
目前电商业务数据分为离线数仓和实时数仓建设,离线和实时数据源,计算引擎和业务代码没有统一,在开发相同需求的时候经常需要离线和实时对齐口径,同时,由于需要维护两套计算路径,对运维也带来压力。
从数据源,业务逻辑,计算引擎完成统一,提高开发和运维效率。