批计算 不等于 离线。 实时 批计算— 快速得到结果 。MySQL OLAP 。
有界数据流—>>>批计算
应用场景:
事件驱动型 应用。
数据分析型 应用
数据管道应用
滚动聚合 全量 聚合。
多级API 。 table api = ==DSL 槽位资源可扩展。任务示例可扩展。
每个算子 都可以成为一个独立的任务
入门程序WordCount
基本Source 算子
Kafka 只有生产者有事务 消费者没有事务机制。
Flink 批计算。
map 与mapPartitions:
两个算子在调用函数时的机制不一样。
一个是元素 一个是迭代器 连接数据库。
RuntimeExecutionMode :流模式 和批模式。
Flink中 使用 lambda 表达式---->>> 单方法接口的方法实现 函数式接口。
lambda 表达式 泛型问题。keySelector 接口。
传入TypeHint 或 TypeInformation。Types 生成 类型对象。
并不是很方便。相对而言 优势并不明显。泛型是一种参数 确切来说是泛型参数。
匿名内部类 相对而言更好。
相关算子:
—Source
----Kafka Source 支持精确一次语义。
开启了Kafka 底层消费值的自动位移提交机制,他会把最新的消费位移提交到kafka的consumer_offsets中,就算吧自动位移提交机制开启,KafkaSource 依然不依赖于自动位移提交机制,优先从flink自己的状态中去获取偏移量,更可靠。
自己提交的偏移量 做监控使用。
addSource和fromSource:
addSource 接受的是SourceFunction的对象 socket 底层 addSource fromCollection fromElement
fromSource 接受的是Source 接口的对象。kafka Source
***** 自定义的Source 算子
SourceFunction
RichSourcFunction: 富函数。
有runtimeContext 和open close 生命周期方法。
生命周期 方法: MapReduce —>>> MR. 线程 方法。
flink 运行的task 实例 称为subtask 。
ParallelSource Function 并行的Source 。
----Transformation
*** map。 flatmap 。filter 。 project 投影【java中有这个方法】
**** keyBy minBy min。滚动聚合算子 在 keyedStream 上操作。
min :返回数据的拼接,最小值+ 第一条数据。
minBy: 返回 一条完整数据。
都是滚动聚合逻辑 max只更新最大值的字段,maxBy更新所有的数据。
**** reduce : 自己定义逻辑。
reduce 只能返回一个值。 如果向要返回多个值 需要看底层的API 。 上层的API 应用简单,但是限制较多,底层API 灵活一些
对比reduce 和sum 底层是 processFunction。
-----利用Reduce 实现Sum
----Sink算子
WriteAsText ~ + WriteMode !
writeAsCsv 行列分隔符。 只能用在元组流上,JavaBean 没有实现。Bean对象 实现不是很方便,元组 字段已经确定。
*****导入log4j的依赖,和配置文件。 看报错的细节
需要刷新到磁盘。csv output format : buffered writer —>>> 4K 缓冲。
底层都是writeUsingOutputFormat 不同的类型。
writeAs 方法均被标记为要被废弃。
******** StreamFileSink: 功能强大,存储分桶。 建议使用。
写入文件中 为了 后门离线计算。 支持分桶存储和列式存储。
Inprogress----- 写入 达到一定状态----->>> pending 挂起状态— 终态Finished File。 三个生命周期------->>>>> …
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-connector-filesartifactId>
<version>1.14.4version>
dependency>
defaultRollingPolicy 指定滚动更新策略:
判定包括 时间和 大小。
BULK 列式存储----->>>>
Parquet-avro .整合。hadoop-common
批模式
流模式 需要加入checkpoint 才可以落盘。
行式存储::forRowFormat
分桶 支持列式存储 这里的分桶是吧不同文件写入文件夹。
forBulkFormat :一批整体编码 整体 写入。整体编码 压缩率高。
一堆小文件 压缩率高?还是总和大小一样的一个大文件------>>>>>. 后者压缩率高。
后面还可以生成列式存储文件。
&&&& 行列之间存储的博弈%%%%%
数仓之中 列式存储 更加合适?
orc parquet :列式存储。
FileSink.forRowFormat();
FileSink.forBulkFormat();
StreamSource.map(JSON::toJSONString).sinkTo();
addSink : 后面加的是Sink Function
sinkTo: 后面加的是Sink 接口的实现类
需要开启ckpt 机制做一个整合。
列格式:
&&&&
write Factory
----ParquetAvroWriter.forGenericRecord. forReflectRecord forSpecificRecord
parquet 文件自带Schema
手动构造Schema。
Avro 的Schema
用avsc 文件。配置结果。 描述文件生成Java。Bean
forReflectRecord:根据Java Bean 反射。
forSpecificRecord :传入特殊的 Bean 即可。
自动生成avro Bean 代码。
bulk 模式下 文件滚动策略只有一种: 当发生ckpt 时进行滚动。
bulk 模式下不能根据文件大小 时间间隔进行滚动—>> 是一个整体。
map
person 是Stu 的父类。List 不是List 父类
Scala中可以:
协变。person 是Stu 的父类。List 是List 父类
逆变 // 不变。
协变 的容器类型。。
3 种方式 对比:
1. 自己构造Schema
2. 特殊的Avro Bean----->>>>. avsc 生成 Avro 类型
3. Java Bean ------>>>>> Reflect bean
生产中 常用的 支持EOS 语义的 按照时间段 将文件划分子文件夹。
既可以输出行文件 又可以输出列文件。
列存储:
若干行 分为一个行组。parquet 本身没有定死 序列化的方式,
avro 序列化。
StreamingFileSink sink = StreamingFileSink.forRowFormat(
new Path(savePath),
new SimpleStringEncoder<String>("UTF-8")
).withRollingPolicy(policy)
.withBucketAssigner(new DateTimeBucketAssigner("'dt='yyyyMMdd/'hour='HH", ZoneId.of("Asia/Shanghai")))
.withOutputFileConfig(config)
.build();
需要构造一个 列模式的FileSink 需要构造一个ParquetAvro Writer Factory 。
//总结 StreamFileSink ----->>>>>>>.<<<<<<<--------
schema 需要序列化。
Hive 解析Avro 序列化某些版本 会有问题 解析Map 问题。
Configuration configuration = new Configuration();
configuration.setInteger("res.port", 8822);
StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(configuration);
//StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.enableCheckpointing(5000, CheckpointingMode.EXACTLY_ONCE);
env.getCheckpointConfig().setCheckpointStorage("/Users/wangqianyu/software/programfoFlinkLearn/NewFlinkDoit/src/main/resources");
DataStreamSource<String> streamSource = env.socketTextStream("localhost", 9999);
KafkaSink<String> kafkaSink = KafkaSink.<String>builder()
.setBootstrapServers("localhost:9092")
.setRecordSerializer(KafkaRecordSerializationSchema.<String>builder()
.setTopic("topic-01")
.setValueSerializationSchema(new SimpleStringSchema())
.build()
).setDeliverGuarantee(DeliveryGuarantee.EXACTLY_ONCE)
.setTransactionalIdPrefix("doit-1001")
.build();
streamSource.sinkTo(kafkaSink);
env.execute("flink-kafka-sink");
source :1个并行度—>>>> 自定义的并行度 是1. 后面是12 也可以startNewChain。
保证EOS。/不保证EOS
实现幂等性: on dulpicate key ~。
MySQL 不支持同一个连接上存在多个并行的事务 必须设置withTransactionPerConnection 为true
Oracle 支持多个。
bahir。flink/spark 扩展。
各种sink 就是对各种数据存储客户端的封装。
Redis 基础数据结构:
String 和 Hash 不同。 大key Hash。 小key String 。。 对于不同类型 API 同名不同意!!!!
-------- 总结-------
----- 侧输出流 processFunction
分流–> deprecated. ------->>>>> outPutSelector ++++ select .
connect 操作。
******* 两个流连接为一个流 共享状态 但是互相独立处理
coFlatMapFunction ~ coMapFunction~
******* 最大的意义在于广播 状态 共享状态 ********
Union 合并
类似于SQL 中两个表的 Union 。
对比Connect:Connect 两个流得数据类型可以一样 也可以不一样。
Union:必须相同。
CoGroup 协同分组---->>>
Join 底层。
where + KeySelector equalTo ~。 无界流 ---->>>> 有界流. 只支持两个流操作。94
迭代器 是 窗口中的一组。
必须要开窗口 window~。
TumblingProcessingTimeWindow ~。 TumblingEventTimeWindow ~。
s1.coGroup(s2).where.equalTo.window.apply(new coGroupFunction~)
使用coGroup :
验证左外连接~~~~
Join 关联操作
join ~ where ~ equalTo~ window ~ apply ~
***********窗口相关:~ 后面讲。
join 是 inner join 如果想left 或right join :用coGroup
apply + flatJoinFunction ~
广播broadcast:
把广播流得数据放到每个主流的实例中去。
关联字典表。或者维表。相当于Map端的Join 。 把数据打宽。
-----------主流 :用户事件流
-----------广播流:维度信息。
processElement------处理主流数据
processBroadcastElement------ 处理广播流数据
自己维护状态程序奔溃后。状态容易丢失。Flink内部封装的状态 容错性好。
事件驱动型应用— 用process 用的比较多。
不同流的类型之间相互转化。
process 可以做测流输出 生命周期方法。拿到运行时上下文信息。
keyed Process Function~/
相关算子~~~
Join coGroup
广播流。------ process Function。
2022-09-11
streamGraph-----jobGraph-----executionGraph—物理执行图
同一个task的运行实例不能放在同一个task slot 中
一个task slot 可以运行多个不同的task
task— 算子的封装
第一个task的结果传给第二个task? 两个task进行网络数据传输。—>>>> 两个算子逻辑放在同一个类中。
多个算子的逻辑放在 一个Task中调用----->>>
算子链。
task 代表一段 逻辑---->>>> sub task 代表运行实例。
并行度----->>>>>>
算子并行度传递规律
节省网络传输---- 节省线程资源,
keyBy是一个独立的任务。
同一个task 的多个并行实例不能放在同一个task slot中。
并行度最大的槽位数<=总槽位数。
以槽位为最小调度单位。 一个task slot 可以运行多个不用task 的1个并行实例。
分区规则:
分区partition 算子。顶层 channel Selector ~/
global ----
broadcast—
forward 1对1。。---- shuffle。 rebalance ---- rescale----
partitionCustom ~ 自定义
定义上下游数据分发规则。—
shuffle :随机发送。
rebalance 轮询发送。
rescale:上下游并行度是 倍数关系。 自己组中轮询---->>>
keyBy设置不了并行度。分区算子 而不是计算算子。
默认发数据 的规则 是轮询— rebalance。
算子并行度: main 方法只是一个客户端。 可以算子绑定的条件; 1.one to one 2.并行度相同 3. slot sharingGroup 同一个槽位共享组。
处理时间。事件时间。
事件时间的推进 。 单调递增不可回退 事件不动数据不动时间不动
处理时间语义:
事件时间语义:
时间窗口 定时器需要用户自己定义处理.
-----时间推进机制.
1.12+ 默认是 事件时间的语义.
事件时间 语义中 有迟到乱序的概念。
推进时间 广播到下游. 数据流中既有 数据还有WaterMark. 虽然没有数据过来但是还是会每隔一定时间 发送 WaterMark.
watermark 和事件时间语义。
时间的推进策略。WaterMark ~ 数据 + WaterMark 。时间戳 会向下游做广播。
一个并行度 取最大
多个并行度 来源数据 取最小。 多个并行度的WaterMark 最终结果为最小的.
时间窗口到达 触发计算逻辑. 假设 每一个窗口 都有一个桶 存储当前窗口内的数据,
WaterMark Idle timeout :某个分区一直没有数据进来,导致WaterMark 没有推进 会强制推进WM。防止数据大量积压。
理解事件时间推进机制---->>> 不同的时间语义。 watermark 和时间窗口统计。
周期性插入 带有时间戳的 WM 。 源头一般是Source 算子。 迟滞时间设置在源头设置。
watermark = 收到的最大时间- 容错时间. 可以容忍 的乱序时间。
WM 可以选择在任何节点生成,一般都在源头生成。
ctx 中可以得到watermark。
processElement 方法 是在什么时候触发??
----- 收到数据后才会调用 没有数据输入 不会触发。 打印时机—>> 打印的WM 是上一条的WM 但是WM会更新。打印的是旧的。 WaterMark 在数据后面会到达。
滚动聚合 aggregate
全窗口聚合: process~
经典 分组TopN 。30 s内分组。
可以用滚动聚合算子 Top 2 .但是返回值 只能输出一条信息,需要将两条做一个拼接。
全窗口---->>>> top N~。
keyBy+ window +apply
keyBy+window+ processWindowFunction ~
apply 和process 对比:
process 中信息更多 含有ctx 可以做测流输出。可以拿到状态。 getRuntimeContext~。 实现Rich Function接口。
apply 相对局限 直接继承自Function。 没有很多的信息。
滚动 滑动 会话窗口 。
全局窗口 key的window 事件时间 处理时间语义。
会话窗口 根据session Gap 划分窗口。
窗口触发器。
flink的默认序列化 不是JDK的序列化 默认用的是avro 序列化。
allowedLateness(2):
如果WaterMark 此刻的事件时间推进到了A窗口结束点后2s 如果还来A窗口的数据,就算迟到 不会再触发A窗口的计算,而是输出到测流迟到流了。
Trigger 和Evictor
onElement 判断是否要触发 触发前 触发后~
Evictor Before + + Evictor After
移除数据。
Evictor : 工作机制:移除本次窗口计算中想移除的数据。算子调用Trigger 发现满足触发逻辑时 会先调用Evictor Before 计算之前移除。
而后计算,计算后Evictor After 进行数据清理。 一般是Evictor Before 。
自己的状态raw状态。 Flink管理:托管状态。
算子状态 :ListState 一般source端会用到 。需要实现checkpointedFunction 接口 snapshot state .initializeState. ListState/UnionListState .
UnionListState 需要人工指定算子重分配。
键控状态:按照Key来进行分组绑定状态。
ListState valueState MapState
自动容错Task级别故障 开启Checkpoint 和重试机制。
checkpoint 存储。定期ckpt。
状态数据在重启后的重分配:~. keyed State 也会有重分配的问题。不会有逻辑的错误
ListState MapState ReducingState AggregatingState 相关API
…
iterable 和iterator。
1.Iterator接口的核心方法next()或者hashNext(),previous()等,都是严重依赖于指针的,也就是迭代的目前的位置。如果Collection直接实现Iterator接口,那么集合对象就拥有了指针的能力,内部不同方法传递,就会让next()方法互相受到阻挠。只有一个迭代位置,互相干扰。
2.Iterable 每次获取迭代器,就会返回一个从头开始的,不会和其他的迭代器相互影响。
3.这样子也是解耦合的一种,有些集合不止有一个Iterator内部类,可能有两个,比如ArrayList,LinkedList,可以获取不同的Iterator执行不一样的操作。
状态TTL: 详解。
数据的存活时长管理:Redis TTL
updateTtlOnReadAndWrite 更新TTL.
updateTtlOnCreateAndWrite
刷新TTL 计时。 插入更新时刷新计时。
setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) // 不允许返回已过期但尚未被清理的数据
.setStateVisibility(StateTtlConfig.StateVisibility.ReturnExpiredIfNotCleanedUp) // 允许返回已过期但尚未被清理的数据
异步线程 定期清除。 过期未被清除: evictor 清除 。底层会做过滤。 超时不返回。
TTL 是 默认 按照事件时间。实际用的是处理时间。
数据清理策略:
.cleanupIncrementally(1,false) // 增量清理(每当一条状态数据被访问,则会检查这条状态数据的ttl是否超时,是就删除)
.cleanupFullSnapshot() // 全量快照清理策略(在checkpoint的时候,保存到快照文件中的只包含未过期的状态数据,但是它并不会清理算子本地的状态数据)
过滤器 过滤 过期数据 本地的数据没有改动。
RockDB 使用。
//.cleanupInRocksdbCompactFilter(1000) // 在rocksdb的compact机制中添加过期数据过滤器,以在compact过程中清理掉过期状态数据
HashMap + RockDB
HashMap : runtime Heap +Disk
RockDB: 内嵌DB KV数据库。 数据不是以对象形式存在,以序列化形式存在。
状态功能的具体实现,
两种StateBackend 存到HDFS 上的快照格式是一样的 可以切换。 兼容SBK。
RockDB – compact [Hbase compact]
Hbase 相关。minor compact major compact。
不设置清理策略会有奇怪事情发生~~~。~~~。
状态功能的具体实现,
两种StateBackend 存到HDFS 上的快照格式是一样的 可以切换。 兼容SBK。
RockDB – compact [Hbase compact]
Hbase 相关。minor compact major compact。
不设置清理策略会有奇怪事情发生~~~。~~~。
UnionList 不需要人工指定 也是自动重分配[勘误]。broadcast 广播模式。 下游每一个获得 所有状态数据。
ListState 模式采用round robin方式
ckpt是为了解决系统崩溃时如何恢复
checkpoint 是保证:
1.故障重启后 各个算子能恢复到统一的状态 经过了相同的数据影响之后 的状态
2. 数据不会被漏处理 不会丢失
定时器 去检查状态数据 是否过期。
三种策略:
增量清除
代码做过期检查。 针对本地状态空间做清除
cleanFullSnapShot本地状态空间没有做清理.
生成的快照结果文件 不会有过期数据 针对快照生效。不去管理本地空间
TTL参数设置:
.setUpdateType(StateTtlConfig.UpdateType.OnReadAndWrite) // 设置ttl计时重置的策略 设置策略 为最后一个生效 setValue形式.
状态可见性:
过期的检查清除策略: 不是覆盖 针对不同的场景. 写三种 都生效. key的形式put.
默认清除策略:
.cleanupIncrementally(1,false) // 增量清理(每当一条状态数据被访问,则会检查这条状态数据的ttl是否超时,是就删除)
每个subtask 都有自己的状态空间.HashMapStateBackend.
ListState ValueState 使用过程中 都将状态放在HashMap中. CopyOnWriteStateMap ~Flink自己设计的结果.
里面放入KV.
List: K:List<>
Value K:value 大key是keyBy的key
cleanUpSize: 每次迭代去检查多少个 key的State 访问状态 会驱动代码 进行清理.
True or false:
数量数达到批次数次才可以. true 每条数据都要处理.
容错
核心流程,
source : 记录偏移量。 可以回滚。KafkaSource 。自己保存偏移量。
Flink: checkpoint保证 一条或者一批数据 要么是经过了完整的处理 如果失败 重启恢复后 所有算子的state 数据都能回到这条数据从未处理过的状态。
Sink端保证:
采用幂等写入的方式
—采用两阶段提交的方式
采用预写日志提交的方式。
Hbase 的事务:行级事务。一行之中 各个字段保持原子性。
状态的精确一次。State的精确一次。
checkpoint 算法关键点:
barrier分段思想。
source 端需要数据重放。
barrier 是source 算子定期插入。算子做完快照后需要向JobManager 做一个应答。
JM 收到对所有算子 的应答。之后 任务这次ckpt是成功的。本身就是一个两阶段 协议。
1) barrier 会在数据源流入源头被注入并行数据流中。
2)Barrier 接着向下游传递
3)一旦sink算子接收到barrier算子 有两种情况
引擎内严格一次处理保证
Sink算子 收到了所有上游的barrier-n时,sink算子对自己的state 进行快照 然后通知检查点协调器 当所有算子 都向检查点协调器汇报成功之后 检查点协调器向所有算子确认本次快照完成。
端到端 严格一次处理保证:
Sink算子已经收到上游所有的Barrier-n算子 时 Sink算子对自己的State 进行了快照并预提交事务。 再通知检查点协调器 检查点协调器向所有算子确认本次。快照的完成 Sink算子 提交食物。两阶段的第二阶段,本次事务完成。
ckpt的对齐和不对齐。
多并行度 多条流的Checkpoint barrier。对齐。。
checkpoint 等待。阻塞 缓存。
非对齐的ckpt。
ckpt对齐-----阻塞传递。背压。 数据积压。 逐级传递 背压 情况。 数据处理效率 很低。 非对齐 只能保证at least once。
说明:
ckpt 机制的调用流程实质是2PC。JobMaster 是协调者 所有Operator task 是执行者。start ckpt是pre-commit的开始信号 而每个operator task 的ckpt是 pre-commit 的过程。ack是 operator task反馈给协调者JobMaster 最后callback 是commit。
tolerableCkptFailure Number 允许失败的最大次数。
ckpt 数据会更新。
对齐超时时间。: 超时后 失败。
setCheckpointInterval :
最大并行的ckpt数: 允许同时存在的ckpt数量
sink 端的 容错策略:
2PC:
两阶段。事务
两阶段 预写日志提交。
幂等性写入可以实现最终一致。但是会有过程中的不一致。
如果一批数据 在两次运行中 计算逻辑产生的结果是不确定的。 随机数Random
kafka 幂等机制:
生产者幂等 producer API 发送到Broker时会失败,producer 会自动重试
利用了序列号 producer 序列号作为幂等性保证。
kafka 支持事务写入:
伪事务。
两阶段:
第一阶段: 开启事务。正确输出数据 barrier 到达 预提交事物。存储本次对外事务号 以及事务状态 pending
做local checkpoint 向 jobmanager 上报
等待notify
第二阶段:
notify 到达
提交事务 向外部系统 commit。如果成功 则修改事务状态 finished。
预写日志的两阶段提交方式。
Task 自动重启策略:
fixDelayRestart
noRestart
exponential delay
失败的failover策略:
Region 策略。 全部重启。或者重启收到影响的最小级。 job中 有两个 完全不相关的流水线。
一个task失败。所有task 都重启? 不一定 ALL/Region
cluster 级别重启
需要从某个快照状态恢复。手动指定savepoint
spark的血缘: 子RDD包含父RDD的引用
Flink 利用图
Spark-sql:
sql—语法树—解析树 逻辑执行计划----逻辑优化树 优化后的逻辑执行计划----物理执行计划 树
----RDD级别的代码。code gen 代码生成。
streamGraph----JobGraph - – Execution Graph---- 物理执行图。
JobManager 把。JobG 变成Execution G 添加并行度。
StreamGraph 转为JobG 在client 端。做算子链聚合Operator Chain 。
物理执行图 是不存在的 只是运行后的一个效果。
Flink-Session Mode
Application Mode
PerJob Mode
maven-shaded-plugin。将依赖打入Jar包
flink 访问HDFS 需要两个。 jar包
flink-shaded-hadoop3-uber-3.1.1.7.2.9.0-173-9.0.jar
commons-cli-1.4.jar
Flink Metrics
8032 请求ResourceManager。8020:NameNode 请求
1.14.4 -t remote。 默认提交到yarn 加入这个参数 可以提交到standalone 集群。
session 模式集群 资源 隔离度 不够 所有任务共享集群资源 共享Job Manager
Yarn 提供的是 yarn container 最轻量级的 它里面只能运行进程
K8s docker 提供的是轻量级的虚拟机 里面可以运行简化版操作系统
VmVare重量级 虚拟机
Yarn 做不到CPU的完全隔离。
yarn 不同模式:
集群的生命周期和资源的 隔离保证
session 模式: 多个Job共享一个JobManager Job退出 集群不会退出。
PerJob模式:每个Job独享集群。Job退出集群退出。 main方法在client端运行
ApplicationMode: 每个Job 独享一个集群 job退出则集群退出 main方法在集群中运行。-----生产环境建议
session 模式: 适应于多次提交小Job的场景。
perJob 和Application 模式需要重新申请JM TM 比较耗时。
Yarn 资源调度策略:
Fair。FIFO【过期】 Capacity。默认
启动Session-cluster 之后不需要指定taskManager 个数。执行任务时 会根据需要 申请资源。 老版本是 固定的 数目 现在可以根据需求灵活。
内存分配的最小单位:
session 模式需要传入yarn application 的Id。-d detach 分离模式。
standalone–standaloneSessionCluster-EntryPoint
PerJob模式: 集群启动和Job提交合二为一。
Application Mode :
Session On Yarn:
集群运行的Job需要多少资源 就动态申请多少资源。
集群运行的Job被cancel 那么占用的yarn资源 也会随之被释放。
集群的JobManager 会一直存在。
Per-job模式。
没有submit 不能提交新Job
Application Mode
Yarn Application Cluster EntryPoint