1.12 Flink
1.12.1 Flink基础架构组成?
Flink程序在运行时主要有TaskManager,JobManager,Client三种角色。
JobManager是集群的老大,负责接收Flink Job,协调检查点,Failover 故障恢复等,同时管理TaskManager。 包含:Dispatcher、ResourceManager、JobMaster。
TaskManager是执行计算的节点,每个TaskManager负责管理其所在节点上的资源信息,如内存、磁盘、网络。内部划分slot隔离内存,不隔离cpu。同一个slot共享组的不同算子的subtask可以共享slot。
Client是Flink程序提交的客户端,将Flink Job提交给JobManager。
1.12.2 Flink和Spark Streaming的区别
Flink Spark Streaming
计算模型 流计算 微批次
时间语义 三种 没有,处理时间
乱序 有 没有
窗口 多、灵活 少、不灵活(窗口长度必须是 批次的整数倍)
checkpoint 异步分界线快照 弱
状态 有,多 没有(updatestatebykey)
流式sql 有 没有
1.12.3 Flink核心概念
1)Task、Subtask的区别
Subtask:算子的一个并行实例。
Task:Subtask运行起来之后,就叫Task。
2)算子链路:Operator Chain
Flink自动做的优化,要求One-to-one,并行度相同。
代码disableOperatorChaining()禁用算子链。
3)Graph生成与传递
在哪里生成 传递给谁 做了什么事
逻辑流图StreamGraph Client Client 最初的DAG图
作业流图JobGraph Client JobManager 算子链路优化
执行流图ExecutionGraph JobManager JobManager 并行度的细化
物理流图
4)并行度和Slot的关系
Slot是静态的概念,是指TaskMangaer具有的并发执行能力。
并行度是动态的概念,指程序运行时实际使用的并发能力。
设置合适的并行度能提高运算效率,太多太少都不合适。
5)Slot共享组了解吗,如何独享Slot插槽
默认共享组时default,同一共享组的task可以共享Slot。
通过slotSharingGroup()设置共享组。
1.12.4 你们公司Flink任务提交模式? Flink部署多少台机器?
(1)我们使用yarn per-job模式提交任务,基于Yarn模式会动态申请资源:
启动的TaskManager数量 = ceil(job并行度 / 每个TaskManager的Slot数)
(2)基于Yarn的per-job模式,Flink只起客户端的作用,理论只需要一台即可,公司一般将命令封装成脚本调度执行,所以部署DolphinScheduler的Worker节点都需要部署Flink的客户端。
1.12.5 Flink任务的并行度是怎样设置的?资源一般如何配置?
设置并行度有多种方式,优先级:算子>全局Env>提交命令行>配置文件
1)并行度根据任务设置:
(1)常规任务:Source,Transform,Sink算子都与Kafka分区保持一致
(2)计算偏大任务:Source,Sink算子与Kafka分区保持一致,Transform算子可设置成2的n次方,64,128…
2)资源设置:通用经验 1CU = 1CPU + 4G内存
Taskmanager的Slot数:1拖1(独享资源)、1拖N(节省资源,减少网络传输)
TaskManager的内存数:4~8G
TaskManager的CPU:Flink默认一个Slot分配一个CPU
JobManager的内存:2~4G
JobManager的CPU:默认是1
1.12.6 Flink的三种时间语义
事件时间Event Time:是事件创建的时间。数据本身携带的时间。
进入时间Ingestion Time:是数据进入Flink的时间。
处理时间Processing Time:是每一个执行基于时间操作的算子的本地系统时间,与机器相关,默认的时间属性就是Processing Time。
1.12.7 你对Watermark的认识
水位线是Flink流处理中保证结果正确性的核心机制,它往往会跟窗口一起配合,完成对乱序数据的正确处理。
- 水位线是插入到数据流中的一个标记,可以认为是一个特殊的数据
- 水位线主要的内容是一个时间戳,用来表示当前事件时间的进展
- 水位线是基于数据的时间戳生成的
- 水位线的时间戳必须单调递增,以确保任务的事件时间时钟一直向前推进
- 水位线可以通过设置延迟,来保证正确处理乱序数据
- 一个水位线Watermark(t),表示在当前流中事件时间已经达到了时间戳t,这代表t之前的所有数据都到齐了,之后流中不会出现时间戳t’ ≤ t的数据
1.12.8 Watermark多并行度下的传递、生成原理
1)分类:
间歇性:来一条数据,更新一次Watermark。
周期性:固定周期更新Watermark。
官方提供的API是基于周期的,默认200ms,因为间歇性会给系统带来压力。
2)生成原理:
Watermark = 当前最大事件时间 - 乱序时间 - 1ms
3)传递:
Watermark是一条携带时间戳的特殊数据,从代码指定生成的位置,插入到流里面。
一对多:广播。
多对一:取最小。
多对多:拆分来看,其实就是上面两种的结合。
1.12.9 Flink怎么处理乱序和迟到数据?
(1)Watermark的乱序等待时间。
(2)使用窗口时,可以允许迟到。
(3)迟到特别久的,放到侧输出流处理。
1.12.10 说说Flink中的窗口(分类、生命周期、触发、划分)
1)窗口分类:
Keyed Window和Non-keyed Window
基于时间:滚动、滑动、会话。
基于数量:滚动、滑动。
2)Window口的4个相关重要组件:
assigner(分配器):如何将元素分配给窗口。
function(计算函数):为窗口定义的计算。其实是一个计算函数,完成窗口内容的计算。
triger(触发器):在什么条件下触发窗口的计算。
evictor(退出器):定义从窗口中移除数据。
3)窗口的划分:如,基于事件时间的滚动窗口
Start = 按照数据的事件时间向下取窗口长度的整数倍。
end = start + size
比如开了一个10s的滚动窗口,第一条数据是857s,那么它属于[850s,860s)。
4)窗口的创建:当属于某个窗口的第一个元素到达,Flink就会创建一个窗口,并且放入单例集合
5)窗口的销毁:时间进展 >= 窗口最大时间戳 + 窗口允许延迟时间
(Flink保证只删除基于时间的窗口,而不能删除其他类型的窗口,例如全局窗口)。
6)窗口为什么左闭右开:属于窗口的最大时间戳 = end - 1ms
7)窗口什么时候触发:如基于事件时间的窗口 watermark >= end - 1ms
1.12.11 Flink的keyby怎么实现的分区?分区、分组的区别是什么?
1)Keyby实现原理:
对指定的key调用自身的hashCode方法=》key.hashcode =》keyHash
调用murmruhash算法,进行第二次hash =》MathUtils.murmurHash(keyHash) % maxParallelism =》keygroupid
计算出当前数据应该去往哪个下游分区:
keyGroupId * parallelism / maxParallelism
键组id * 下游算子并行度 / 最大并行度(默认128)
2)分区:算子的一个并行实例可以理解成一个分区,是物理上的资源
3)分组:数据根据key进行区分,是一个逻辑上的划分
一个分区可以有多个分组,同一个分组的数据肯定在同一个分区
1.12.12 Flink的Interval Join的实现原理?Join不上的怎么办?
底层调用的是keyby + connect ,处理逻辑:
(1)判断是否迟到(迟到就不处理了,直接return)
(2)每条流都存了一个Map类型的状态(key是时间戳,value是List存数据)
(3)任一条流,来了一条数据,遍历对方的map状态,能匹配上就发往join方法
(4)使用定时器,超过有效时间范围,会删除对应Map中的数据(不是clear,是remove)
Interval join不会处理join不上的数据,如果需要没join上的数据,可以用 coGroup+join算子实现,或者直接使用flinksql里的left join或right join语法。
1.12.13 介绍一下Flink的状态编程、状态机制?
(1)算子状态:作用范围是算子,算子的多个并行实例各自维护一个状态
(2)键控状态:每个分组维护一个状态
(3)状态后端:两件事=》 本地状态存哪里、checkpoint存哪里
1.13版本之前
本地状态 Checkpoint
内存 TaskManager的内存 JobManager内存
文件 TaskManager的内存 HDFS
RocksDB RocksDB HDFS
1.13版本之后
本地状态
Hashmap() TaskManager的内存
RocksDB RocksDB
Checkpoint存储 参数指定
1.12.14 实时项目当中有没有遇到大状态,如何调优?
(1)使用rocksdb
(2)开启增量检查点、本地恢复、设置多目录
(3)设置预定义选项为 磁盘+内存 的策略,自动设定 writerbuffer、blockcache等
1.12.15 Flink如何实现端到端一致性?
1)一般说的是端到端一致性,要考虑source和sink:
Source:可重发
Flink内部:Checkpoint机制(介绍Chandy-Lamport算法、barrier对齐)
Sink:幂等性 或 事务性 写入
2)我们使用的Source和Sink主要是Kafka,如果要实现端到端精准一次:
(1)作为Source可以重发,由Flink维护offset,作为状态存储,指定隔离级别为READ_COMMITTED才不会消费到未正式提交的数据,避免重复
(2)作为Sink官方的实现类是基于两阶段提交,能保证写入的Exactly-Once,需要指定sink的语义为Exactly-Once生效
3)如果下级存储不支持事务:
具体实现是幂等写入,需要下级存储具有幂等性写入特性。
比如结合HBase的rowkey的唯一性、数据的多版本,实现幂等
结合Clickhouse的ReplicingMergeTree实现去重,查询时加上final保证查询一致性
4)实际项目中,完全的精准一次为影响数据可见性,要等到第二次提交下游才能消费到。
我们使用时只考虑了至少一次,Kafka Source设置的隔离级别是READ_UNCOMMITTED,Kafka Sink也没有使用exactly-once来保证时效性。
1.12.16 Flink分布式快照的原理是什么
barriers在数据流源处被注入并行数据流中。快照n的barriers被插入的位置(我们称之为Sn)是快照所包含的数据在数据源中最大位置。
例如,在Kafka中,此位置将是分区中最后一条记录的偏移量。 将该位置Sn报告给checkpoint协调器(Flink的JobManager)。
然后barriers向下游流动。当一个中间操作算子从其所有输入流中收到快照n的barriers时,它会为快照n发出barriers进入其所有输出流中。
一旦Sink操作算子(流式DAG的末端)从其所有输入流接收到barriers n,它就向Checkpoint协调器确认快照n完成。
在所有Sink确认快照后,意味快照着已完成。一旦完成快照n,Job将永远不再向数据源请求Sn之前的记录,因为此时这些记录(及其后续记录)将已经通过整个数据流拓扑,也即是已经被处理结束。
1.12.17 Checkpoint的参数怎么设置的?
(1)间隔:兼顾性能和延迟,一般任务设置分钟级(1~5min),要求延迟低的设置秒级
(2)语义:默认精准一次
因为一些异常原因可能导致某些barrier无法向下游传递,造成job失败,对于一些时效性要求高、精准性要求不是特别严格的指标,可以设置为至少一次。
(3)超时:参考间隔,默认10min,建议间隔的2倍
(4)最小等待间隔:上一次ck结束 到 下一次ck开始 之间的时间间隔,设置间隔的0.5倍
(5)设置保存ck:Retain
(6)失败次数:次数是累积的,设大一点
(7)Task重启策略(Failover):
固定延迟重启策略:重试几次、每次间隔多久。
失败率重启策略:重试次数、重试区间、重试间隔。
无重启策略:一般在开发测试时使用。
Fallback重启策略:默认固定延迟重启策略。
1.12.18 介绍一下Flink的CEP机制,匹配不上的数据怎么办?
CEP全称为Complex Event Processing,复杂事件处理,定义规则匹配数据。
模式序列:
严格连续 .next()
松散连续 .followedBy()
不确定的松散连续:.followedByAny()
超时没匹配上的数据,会放入侧输出流,select()可以处理 匹配上的、未匹配上的数据。
1.12.19 Flink SQL的工作机制?
通过Calcite对编写的SQL进行解析、验证、优化等操作。
Blink Planner与Calcite进行对接,对接流程如下:
(1)在Table/SQL 编写完成后,通过Calcite 中的parse、validate、rel阶段,以及Blink额外添加的convert阶段,将其先转为Operation;
(2)通过Blink Planner 的translateToRel、optimize、translateToExecNodeGraph和translateToPlan四个阶段,将Operation转换成DataStream API的 Transformation;
(3)再经过StreamJraph -> JobGraph -> ExecutionGraph等一系列流程,SQL最终被提交到集群。
1.12.20 FlinkSQL怎么对SQL语句进行优化的?
1)设置空闲状态保留时间
2)开启MiniBatch
3)开启LocalGlobal
4)开启Split Distinct
5)多维Distinct使用Filter
1.12.21 Flink提交流程、内存模型(重点)
1)Flink提交流程(Yarn-Per-Job)
2)内存模型
1.12.22 Flink反压产生原因&定位&解决(重点)
1)反压的原因
短时间的负载高峰导致系统接收数据的速率远高于它处理数据的速率。许多日常问题都会导致反压,例如,垃圾回收停顿可能会导致流入的数据快速堆积,或遇到大促、秒杀活动导致流量陡增。
2)反压的危害
Kafka数据积压。
Checkpoint超时失败 =》 Job挂掉。
可能伴随OOM。
数据延迟增大。
3)定位反压
(1)利用Web UI定位
定位到造成反压的节点,排查的时候,先把operator chain禁用,方便定位到具体算子。
Flink 现在在UI上通过颜色和数值来展示繁忙和反压的程度。
上游都是high,找到第一个为ok的节点就是瓶颈节点。
(2)利用Metrics定位
可以根据指标分析反压: buffer.inPoolUsage、buffer.outPoolUsage
可以分析数据传输
4)处理反压
反压可能是暂时的,可能是由于负载高峰、CheckPoint 或作业重启引起的数据积压而导致反压。如果反压是暂时的,应该忽略它。
(1)查看是否数据倾斜
(2)使用火焰图分析看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题。
(3)分析GC日志
(4)资源不合理造成的:调整资源
(5)与外部系统交互:
写MySQL、Clickhouse:攒批写入
读HBase:异步IO、旁路缓存
1.12.23 Flink数据倾斜定位、分析、解决(重点)
1)数据倾斜现象:
相同Task 的多个 Subtask 中,个别Subtask 接收到的数据量明显大于其他 Subtask 接收到的数据量,通过 Flink Web UI 可以精确地看到每个 Subtask 处理了多少数据,即可判断出 Flink 任务是否存在数据倾斜。通常,数据倾斜也会引起反压。
2)数据倾斜解决
(1)数据源倾斜
比如消费Kafka,但是Kafka的Topic的分区之间数据不均衡
读进来之后调用重分区算子:rescale、rebalance、shuffle等
(2)单表分组聚合(纯流式)倾斜
API:利用flatmap攒批、预聚合
SQL:开启MiniBatch+LocalGlobal
(3)单表分组开窗聚合倾斜
第一阶段聚合:key拼接随机数前缀或后缀,进行keyby、开窗、聚合
注意:聚合完不再是WindowedStream,要获取WindowEnd作为窗口标记作为第二阶段分组依据,避免不同窗口的结果聚合到一起)
第二阶段聚合:按照原来的key及windowEnd作keyby、聚合
在我们项目中,用到了Clickhouse,我们可以第一阶段打散聚合后,直接写入Click house,查clickhouse再处理第二阶段
1.12.24 Flink常见的维表Join方案
(1)预加载:open()方法,查询维表,存储下来 ==》 定时查询
(2)热存储:存在外部系统Redis、HBase等
缓存
异步查询: 异步IO功能
(3)广播维表
(4)Lookup Join:外部存储,connector创建
1.12.25 FlinkCDC锁表问题
(1)FlinkCDC 1.x同步历史数据会锁表
设置参数不加锁,但只能保证至少一次。
(2)2.x 实现了无锁算法,同步历史数据的时候不会锁表