02-流批OLAP 一体的 Flink 引擎介绍 | 青训营笔记

227 阅读14分钟

02-流批OLAP 一体的 Flink 引擎介绍 | 青训营笔记

这是我参与「第四届青训营 」笔记创作活动的第1天

1-Flink概述

为什么会有流式计算的需求,为什么Flink能够脱颖而出,Flink当前的开源生态

1.1 Apache Flink的诞生背景

1.1.1 什么是大数据

大数据(Big Data):指无法在一定时间内用常规软件工具对其进行获取、存储、管理和处理的数据集合。

大数据的 4V 特性:

  1. Volumes 海量化:数据规模大
  2. Variety 多样化:大数据的数据源和数据种类多样(结构化,半结构化…)
  3. Velocity 快速化:数据产生和处理的速度快
  4. Value 价值化:价值高但是价值密度低

1.1.2 大数据计算架构发展历史

  1. 史前阶段
    • 传统数仓
    • Oracle
    • 单机(IBM大型机)
    • 黑箱使用
  2. Hadoop
    • 分布式
    • Map-Reduce
    • 离线计算
  3. Spark
    • 批处理
    • 流处理
    • SQL高阶API
    • 内存迭代计算
  4. Flink
    • 流计算
    • 实时、更快
    • 流批一体
    • Streaming/Batch SQL

阶段一是单机处理;单机无法处理海量数据,阶段二Google的三驾马车(GFS, MapReduce, BigTable);由于Map和Reduce的过程中数据需要落盘,处理性能较差,阶段三提出了基于内存迭代计算的方案,同时MapReduce中将所有的操作都抽象为Map和Reduce这两种操作,提供的接口比较原始,无法满足其他的需求或者实现较为复杂,Spark提供了基于SQL的高阶API,提供了更多的语义,使用更加简单。业内对于数据的实时性有了更高的要求,需要使用流式计算,并且Flink能够使用SQL统一流和批的操作。

1.1.3 为什么需要流式计算

业务背景:大数据的实时性带来的价值更大。

  1. 监控场景:如果能实时发现业务系统的健康状态,就能提前避免业务故障;
  2. 金融风控:如果实时监测出异常交易的行为,就能及时阻断风险的发生;
  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 流式计算引擎对比

StormSpark StreamingFlink
Streaming ModelNative(一条一条处理)mini-batchNative
一致性保证At Least/Most OnceExactly-OnceExactly-Once
延迟低延迟(毫秒级)延迟较高(秒级)低延迟(毫秒级)
吞吐LowHighHigh
容错ACKRDD Based CheckpointCheckpoint(Chandy-Lamport)
StateFulNoYes(DStream)Yes(Operator)
SQL支持NoYesYes
  1. Streaming Model:Native表示一条数据一条数据处理,实时性更高/mini-batch 微批
  2. 一致性保证:At Least Once(至少一次,有可能多处理,重发)/At Most Once(至多一次,数据只会往下游发一次,出故障不会重发,有可能没有被处理到)/Exactly-Once(恰好一次)
  3. 状态: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各个模块的用途

  1. SDK层:Flink的SDK目前主要有三类,SQL/TableDataStreamPython;(SQL使用方便,DataStream适用于SQL难以表达的场景,比如定时触发,pyFlink主要用于ML的场景)
  2. 执行引擎层(Runtime层):执行引擎层提供了统一的DAG用来描述数据处理的Pipeline,不管是流还是批,都会转化为DAG图,调度层再把DAG转化成分布式环境下的Task(将Task分发到不同的worker节点去执行),Task之间通过Shuffle传输数据;
  3. 状态存储层:负责存储算子的状态信息;
  4. 资源调度层:目前Flink可以支持部署在多种环境。

2.2 Flink总体架构

Master/Slave架构、JobManager/TaskManager

一个Flink集群,主要包含以下两个核心组件:

  1. JobManager(JM):负责整个任务的协调工作,包括:调度Task、触发协调Task做Checkpoint、协调容错恢复等;

  2. 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 为什么需要流批一体

举个例子:

  1. 在抖音中,实时统计一个短视频的播放量、点赞数,也包括抖音直播间的实时观看人数等;
  2. 在抖音中,按天统计创造者的一些数据信息,比如昨天的播放量有多少、评论量多少、广告收入多少;

上述架构有一些痛点:

  1. 人力成本比较高:批、流两套系统,相同逻辑需要开发两遍;
  2. 数据链路冗余:本身计算内容是一致的,由于是两套链路,相同逻辑需要运行两遍,产生一定的资源浪费;
  3. 数据口径不一致:两套系统、两套算子、两套UDF,通常会产生不同程度的误差,这些误差会给业务方带来非常大的困扰。

2.4.2 流批一体的挑战

流和批业务场景的特点如下表:

批式计算相比于流式计算核心的区别如下表:

2.4.3 Flink如何做到流批一体

为什么可以做到流批一体呢?

  1. 批式计算是流式计算的特例,Everything is Streams,有界数据集(批式数据)也是一种数据流、一种特殊的数据流;

因此,理论上我们是可以用一套引擎架构来解决上述两种场景,只不过需要对不同场景支持相应的扩展性、并允许做不同的优化策略。

流和批的shuffle service允许不一样,可插拔的。

站在Flink的角度,Everything is Streams,无边界数据集是一种数据流,一个无边界的数据流可以按时间切段成一个个有边界的数据集,所以有界数据集(批式数据)也是一种数据流。 因此,不管是有边界的数据集(批式数据)还是无边界数据集,Flink都可以天然地支持,这是Flink支持流批一体的基础。并且Flink在流批一一体上,从上面的API到底层的处理机制都是统一的,是真正意义上的流批一体。

Apache Flink主要从以下几个模块来做流批一体:

  1. SQL层;
  2. DataStream API层统一,批和流都可以使用DataStream API来开发;
  3. Scheduler层架构统一,支持流批场景;
  4. Failover Recovery层架构统一,支持流批场景;
  5. 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通常有几种不同的实现:

  1. 基于文件的Pull Based Shuffle,比如Spark或MR,它的特点是具有较高的容错性,适合较大规模的批处理作业,由 于是基于文件的,它的容错性和稳定性会更好一些;
  2. 基于Pipeline的Push Based Shuffle,比如Flink、Storm、Presto等,它的特点是低延迟和高性能,但是因为shuffle 数据没有存储下来,如果是batch任务的话,就需要进行重跑恢复;

shuffle的数据没有落盘,直接存在内存中推给下游。

流和批Shuffle之间的差异:

  1. Shuffle数据的生命周期:流作业的Shuffle数据与Task是绑定的(Task销毁了,数据就没了),而批作业的Shuffle数据与Task是解耦的(上游结束了,shuffle还在);
  2. Shuffle数据存储介质:流作业的生命周期比较短、而且流作业为了实时性,Shuffle通常存储在内存中,批作业因为数据量比较大以及容错的需求,一般会存储在磁盘里;
  3. 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开源社区已经支持:

  1. Netty Shuffle Service:既支持pipeline又支持blocking,Flink默认的shuffle Service策略;
  2. Remote Shuffle Service:既支持pipeline又支持blocking,不过对于pipeline模式,走remote反而会性能下降,主要是有用在batch的blocking场景,字节内部是基于CSS来实现的RSS。

经过相应的改造和优化之后,Flink在架构设计上,针对DataStream层、调度层、ShuffleService层,均完成了对流和批的支持。

至此,业务已经可以非常方便地使用Flink解决流和批场景的问题了。

3-Flink架构优化

流/批/OLAP三种业务场景概述,Flink如何来支持OLAP场景需求,需要做哪些架构上的优化

3.1 流/批/OLAP业务场景概述

在实际生产环境中,针对不同的应用场景,我们对数据处理的要求是不同的:

  1. 有些场景下,只需离线处理数据,对实时性要求不高,但要求系统吞吐率高,典型的应用是搜索引擎构建索引;

三种业务场景的的特点比对如下表:

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上的实践

目前电商业务数据分为离线数仓和实时数仓建设,离线和实时数据源,计算引擎和业务代码没有统一,在开发相同需求的时候经常需要离线和实时对齐口径,同时,由于需要维护两套计算路径,对运维也带来压力。

从数据源,业务逻辑,计算引擎完成统一,提高开发和运维效率。