这是我参与「第四届青训营 」笔记创作活动的第2天
一、Filnk 概述
1. 背景
- 大数据:无法在一定时间内用常规软件工具对其进行获取、存储、管理和处理的数据集合。
- 特点:价值化、海量化、多样化、快速化
- 计算架构发展历史:史前阶段~2006 (单机,传统数仓) Hadoop (分布式,Map-Reduce,离线计算) Spark (批处理、流处理) Flink (流计算、实时、流批一体)
(1)流式计算的意义及特点
- 业内对实时计算的要求更高,数据实时价值更大;
- 大数据批式处理分钟级、小时级、天级,部分业务场景无法接受;
| 批式计算 | 流式计算 |
|---|---|
| 离线计算,非实时 | 实时计算,快速、低延迟 |
| 静态数据集 | 无限流、动态、无边界 |
| 小时、天等周期性计算 | 7*24 h 持续运行 |
| - | 流批一体 |
(2)运用场景
- 监控场景:实时发现业务系统的健康状态
- 金融风控:实时监测出异常交易
- 实时推荐:根据用户浏览的内容实时发掘数据
2. 流式计算引擎对比
- Streaming Model:一次读一条数据(快速)还是批量数据
- 一致性保证:能否做到精确一次计算语义的保证,故障后应正确恢复有状态运算符中的状态
- 容错:作业挂掉,能否恢复之前的信息
- StateFul:状态
- SQL 支持:能否直接用SQL最简单的语义写出流处理作业
Spark Streaming 基于 Spark 批处理框架的一个计算框架,它会把数据做成一个mini-batch,批量处理 Storm 之前有ACK的机制,At Least 只能保证数据至少被处理一次,但有可能会多处理(传输处理失败,重发);Most Once 下游最多处理一次,有可能没有处理到:数据只会向下游发一次,下游有可能没处理完有故障,但是这个数据不会再重发。
Flink和Spark不用依赖外部系统来存储状态,直接利用引擎提供的state功能就可以解决对状态处理的需求。
3. Why Flink
- 精确一次的计算语义 Exactly-Once
- 状态容错 Checkpoint
- Dataflow 编程模型,Window 等高阶需求支持友好
- 流批一体
二、Flink 整体架构
1. Flink 分层架构
-
SDK层:Flink的SDK目前主要有三类:SQL/Table、DataStream、Python;
-
执行引擎层(Runtime 层):执行引擎层提供了统一的 DAG,用来描述数据处理的 Pipeline,不管是流还是批,都会转化为 DAG 图,调度层再把 DAG 转化成分布式环境下的 Task,Task 之间通过 Shuffle 传输数据;
-
状态存储层:负责存储算子的状态信息(RocksDB Backend 写本地磁盘,性能较filesystem优越);
-
资源调度层:Flink 支持在Standalone/Yam/K8s等多种环境中部署。
2. Flink 总体架构
最左端是Client,自己写的 SDK 业务逻辑首先会在Client处理,Client 处理的信息提交给 JobManager(JM),JM再做处理,转化成真正可以运行的物理执行图,并分发到对应的 worker 节点执行。
一个Flink集群有两个核心组件:
- JobManager(JM):负责整个任务的协调工作,包括:调度 task、触发协调 Task 做 Checkpoint、协调容错恢复等。其核心有三个组件:
- TaskManager(TM):Flink 集群的 worker 节点,负责执行一个 DataFlow Graph 的各个 task 以及 data streams 的 buffer 和数据交换
- Dispatcher: 接收作业,拉起 JobManager 来执行作业,并在 JobMaster 挂掉之后恢复作业
- JobMaster: 管理一个 job 的整个生命周期,会向 ResourceManager 申请 slot,并将 task 调度到对应 TM 上。(slot可认为是一个插槽,可放一个task)
- ResourceManager:负责 slot 资源的管理和调度,Task manager 拉起之后会向 RM 注册
【整体流程】 Client 端将用户的数据处理逻辑 pipeline 的内容转换为 DAG(有向无环图)的逻辑执行图并提交给 JM,JM 将其转化为具体的物理执行图,并 JM 根据物理执行图相关逻辑和对应的任务调度把 task 调度到不同的 TaskManager 中去执行。
三、Flink 架构优化
1. 使用流批一体的原因
- 实时统计短视频的播放量、点赞数和直播间观看人数
- 按天统计UP主的数据信息,如昨日播放量、评论数、广告收入等
传统架构的实时数仓部分使用 Flink 来做相关的流式处理,原始的数据都导入到 Kafka 里作为传统的存储系统,然后 Flink 再基于 Kafka里的数据做数据清洗、统计,处理完后再把数据导入到服务层,再通过服务层向抖音上的业务提供数据的展示。
传统架构的离线数仓:通常数据导入到 Kafka 之后再落到 hive 里面,再去做一些按天的处理(离线处理),最终数据导入到服务层,然后用户通过查询来查到这些数据,以给用户提供相关服务。
缺点:
- 人力成本比较高:批、流两套系统,相同逻辑需要开发两遍;
- 数据链路冗余:本身计算内容是一致的,由于是两套链路,相同逻辑需要运行两遍,产生一定的资源浪费;
- 数据口径不一致:两套系统、两套算子、两套 UDF,通常会产生不同程度的误差,这些误差会给业务方带来非常大的困扰。
2. 流式处理和批式处理的业务场景特点
| 流式计算 | 批式计算 |
|---|---|
| 实时计算 | 离线计算 |
| 延迟在秒级以内 | 处理时间为分钟到小时,甚至天级别 |
| 0~1s | 10s~1h+ |
| 广告推荐 | 搜索引擎构建索引、批式数据分析 |
核心区别:
| 纬度 | 流式计算 | 批式计算 |
|---|---|---|
| 数据流 | 无限数据集 | 有限数据集 |
| 时延 | 低延迟、业务会感知运行中的情况 | 实时要求不高,只关注最终结果产出时间 |
- 流式计算是无限数据集,永远都在计算;批式计算是一批计算完成,再去调度下一批的任务继续执行
- 流式计算在任何时间点出现计算慢,都会有感知;批式计算可以在某一个时间点之前完成,只需关心结果
3. Flink 之流批一体
Flink 为何能做到流批一体?
- Everything is Streams,批式计算是特殊的流式计算,有界数据集也是一种特殊的数据流
Apache Flink 主要从以下几个模块来做流批一体:
- SQL 层【支持有边界和无边界的输入】;
- DataStream API 层统一,批和流都可以使用 DataStream API 来开发【特殊的state处理,用sql比较难表达】;
- Scheduler 层架构统一,支持流批场景;
- Failover Recovery 层架构统一,容错,支持流批场景;
- Shuffle Service 层架构统一,流批场景选择不同的 Shuffle Service;
(1)流批一体 Scheduler 层
Scheduler 主要负责将作业逻辑的 DAG 转化为物理执行的图并做一些调度。即在分布式环境中可以执行的 Task。
Flink 的调度模式
【1.12版本之前的调度模型】
| 模式 | 特点 | 场景 |
|---|---|---|
| EAGER | 申请一个作业所需要的全部资源,然后同时调度这个作业的全部 Task,所有的 Task 之间采取 Pipeline 的方式进行通信(数据源源不断) | Stream 作业场景 |
| LAZY | 先调度上游,等待上游产生数据或结束后再调度下游,类似 Spark 的 Stage 执行模式 | Batch 作业场景 |
- EAGER 模式:N 个 task 全部调度起来才能真正运行,集群需要有足够的资源
- LAZY 模式:最小调度一个 task 即可,集群有一个 slot 资源可以运行
【Pipeline Region Scheduler 机制】
- 由 Pipeline 的数据交换方式连接的 Task 构成为一个 Pipeline Region;
- 本质上,流作业和批作业都按照 Pipeline Region 粒度来申请资源和调度任务。
对于流作业,如果流里面全部是无限数据集,那么就认为整个流都是一个 Pipeline Region,则 Pipeline Region 就全会调度起来。
Pipeline Region 的核心就是为了在调度层面能够同时处理流场景和批场景而做的一个抽象。
数据交互模式
ALL_EGDGES_BLOCKING:
- 所有的 Task 之间的数据交换都是 BLOCKING 模式
- 分为 N 个 pipeline region
ALL_EGDGES_PIPELINE:
- 所有的 Task 之间的数据交换都是 PIPELINE 模式
- 分为 1 个 pipeline region
Blocking 模式:数据不会直接传输给下个节点,需要先落盘的数据交互方式。
- 例如 A产生的数据不会立刻发给B,中途需要先写入磁盘,这样 A 结束之后,B 依然可以从文件中读取 A 输出的数据。
Pipeline 模式:数据产生后直接传输给下个节点。
- 例如 A 产生数据后直接存入内存发给B,数据不做存储直接释放。
(2)流批一体 Shuffle Service 层
Shuffle:在分布式结算中,用来连接上下游数据交互的过程。实际上,分布式计算中所有涉及到上下游衔接的过程,都可以理解为 Shuffle。
例如 A 到 B,B 到 C,C 到 D 的每一个过程都是一个 shuffle。只要涉及到上下游的数据交换,都是一个 shuffle。
Shuffle 分类
- 基于文件的 Pull Based Shuffle,比如 Spark 或 MR,它的特点是具有较高的容错性,适合较大规模的批处理作业,由于是基于文件的 【类似 Blocking 模式】,它的容错性和稳定性会更好一些;
- 基于 Pipeline 的 Push Based Shuffle,比如 Flink、Storm、Presto 等,它的特点是低延迟和高性能,但是因为 shuffle 数据没有存储下来,如果是 batch 任务的话,就需要进行重跑恢复。
流和批 Shuffle 之间的差异
- Shuffle 数据的生命周期:流作业的 Shuffle 数据与 Task 是绑定的,而批作业的 Shuffle 数据与 Task 是解耦的;
- 流:如果Task销毁了,shuffle 数据也没有了
- 批:如果上游的Task跑完了,结果写入文件,Task生命周期结束释放资源,但是其产生的shuffle生命周期还在
- Shuffle 数据存储介质:流作业的生命周期比较短、而且流作业为了实时性,Shuffle 通常存储在内存中,批作业因为数据量比较大以及容错的需求,一般会存储在磁盘里;
- Shuffle 的部署方式:流作业 Shuffle 服务和计算节点部署在一起,可以减少网络开销,从而减少 latency,而批作业则不同。
| Streaming 和 OLAP 场景 | Batch 场景 |
|---|---|
| 通常使用基于 Pipeline 的 shuffle 模式 | 一般选取 Blocking 的 shuffle 模式 |
(3)流/批/OLAP 业务场景
| 流式计算 | 批式计算 | 交互式分析 |
|---|---|---|
| 实时计算 | 离线计算 | OLAP |
| 延迟在秒级以内 | 处理时间为分钟到小时,甚至天级别 | 处理时间秒级 |
| 0~1s | 10s~1h+ | 1~10s |
| 广告推荐 | 搜索引擎构建索引、批式数据分析 | 数据分析BI表报 |
交互式分析(OLAP) 实时性高、查询延迟在秒级,但要求高并发查询。
批式计算是流式计算的特例,OLAP计算是一种特殊的批式计算,对并发和实时性要求高,其他情况与普通批式作业没有特别大区别。因此可用一套引擎架构来解决上述三种场景,只不过需要对不同场景支持相应的扩展性和做不同的优化策略。
| Batch 场景需求 | OLAP 场景需求 |
|---|---|
| 流批一体支持: - Unify DataStream API - Scheduler - Shuffle Service - Failover Recovery | 短查询作业场景: - 高并发支持; - 极致处理性能 |
Flink OLAP 场景的挑战
- 秒级和毫秒级的小作业
- 作业频繁启停、资源碎片
- Latency + 高 APS 要求
四、案例
1. 电商流批一体
首先数据导入到 Kafka 里,经过 Flink Stream 预处理之后,经过 Kafka to Hive 层,将数据源同步到 Hive 的仓库里,使用Flink Batch 直接从 Hive 里面处理数据,然后导入到下游的服务层。基于 Kafka 同样的数据源,在Flink Stream做流式处理,再导入下游服务层。
2. 字节 Flink OLAP
Flink 的 OLAP 在字节内部的场景主要是 HTAP 场景。字节内部HTAP的AP查询部分,是由Flink提供能力的。
- 上面是原来的链路:线上的数据库通过离线的流程把数据离线同步到数据源里,下游再基于Hive的离线数据源做处理,有小时或天级别的延迟。【特点:在/离线完全隔离】
- 下面是走HTAP之后的链路,Flink 直接提供数据查询与分析的能力:秒级甚至更低的程度。