自己学习Flink时整理的一些笔记,还不是很完整,后续慢慢增改,一起学习,有不同的看法或者意见可以私信或者评论
1 Flink概述
Flink是一个基于流式数据的有状态计算框架
Flink是一个分布式的大数据处理计算引擎/框架, 支持流式数据处理(当然也支持离线),支持有状态计算(当然也支持无状态),支持基于事件时间的延迟数据处理,支持复杂事件处理(如CEP)....能够做到低延迟,高吞吐...
1.1 Flink的特点:
(1)相对于Spark的流处理,Flink更优越
注意:
Spark的流处理:
SparkStreaming是用批处理的思想做流处理(微批处理),或多或少会有时间延迟!做不到毫秒级,一般也就秒级
StructuredStreaming可以做微批处理,也可以做连续处理(还不稳定,功能有很多限制)
Flink的流处理:
Flink是真正的流处理,可以做到基于事件的实时数据处理
(2)Flink支持多种灵活的时间窗口
(3)Flink支持复杂事件处理(CEP)
(4)支持基于事件时间的延迟数据处理(StructuredStreaming也支持,但是不够强大)
(6)支持State和Checkpoint(SparkStreaming也支持,但是不够强大)
......
1.2Flink原理
Flink的组件栈
1.2.1 Flink四大基石
1.2.2 Flink执行流程
Standalone版本
1.用户提交任务给JobClient
2.JobClient发送任务给JobManager
3.JobManager返回提交成功
4.JobManager将任务分发给TaskManager执行
5.TaskManager汇报任务的执行状态给JobManager
6.任务执行结束JobManager返回执行结果给JobClient
OnYarn版本
1.Client上传Flink任务的jar包和配置到HDFS
2.Client向Yarn(ResourceManager)提交任务并申请资源
3.ResourceManager分配并启动ApplicationMaster并读取HDFS上的Flink任务配置信息
4.ApplicationMaster根据任务配置信息给Flink任务分配资源启动JobManager和TaskManager
5.JobManager将任务分发给TaskManager执行(TaskManager执行时需要去HDFS上读取任务jar包)
1.2.3 Flink角色分工
FlinkClient:
主要职责:(类似Driver)
接收用户的提交的程序代码,然后创建DataFlow模型(类似于DAG),将DataFlow数据流提交给JobManager
JobManager:
主要职责:(类似于Driver+Master)
负责整个Flink集群任务的调度和Flink内部资源的管理,包括调度计算任务task,管理Checkpoint....
TaskManager:
主要职责:(类似于Worker/Executor)
负责具体的任务的执行
2 批处理DataSet
2.1 批处理-Data Sources
2.1.1基于集合
(1)使用env.fromElements()支持Tuple,自定义对象等复合形式。
(2)使用env.fromCollection()支持多种Collection的具体类型
(3)使用env.generateSequence()支持创建基于Sequence的DataSet
2.1.2 基于文件
(1)读取本地文件数据
(2)读取HDFS文件数据
(3)读取CSV文件数据
(4)读取压缩文件
2.2 批处理-Transformation
Transformation | Description |
Map | 在算子中得到一个元素并生成一个新元素data.map { x => x.toInt } |
FlatMap | 在算子中获取一个元素, 并生成任意个数的元素data.flatMap { str => str.split(" ") } |
MapPartition | 类似Map, 但是一次Map一整个并行分区data.mapPartition { in => in map { (_, 1) } } |
Filter | 如果算子返回true则包含进数据集, 如果不是则被过滤掉data.filter { _ > 100 } |
Reduce | 通过将两个元素合并为一个元素, 从而将一组元素合并为一个元素data.reduce { _ + _ } |
ReduceGroup | 将一组元素合并为一个或者多个元素data.reduceGroup { elements => elements.sum } |
Aggregate | 讲一组值聚合为一个值, 聚合函数可以看作是内置的Reduce函数data.aggregate(SUM, 0).aggregate(MIN, 2)data.sum(0).min(2) |
Distinct | 去重 |
Join | 按照相同的Key合并两个数据集input1.join(input2).where(0).equalTo(1)同时也可以选择进行合并的时候的策略, 是分区还是广播, 是基于排序的算法还是基于哈希的算法input1.join(input2, JoinHint.BROADCAST_HASH_FIRST).where(0).equalTo(1) |
OuterJoin | 外连接, 包括左外, 右外, 完全外连接等left.leftOuterJoin(right).where(0).equalTo(1) { (left, right) => ... } |
CoGroup | 二维变量的Reduce运算, 对每个输入数据集中的字段进行分组, 然后join这些组input1.coGroup(input2).where(0).equalTo(1) |
Cross | 笛卡尔积input1.cross(input2) |
Union | 并集input1.union(input2) |
Rebalance | 分区重新平衡, 以消除数据倾斜input.rebalance() |
Hash-Partition | 按照Hash分区input.partitionByHash(0) |
Range-Partition | 按照Range分区input.partitionByRange(0) |
CustomParititioning | 自定义分区input.partitionCustom(partitioner: Partitioner[K], key) |
First-n | 返回数据集中的前n个元素input.first(3) |
partitionByHash | 按照指定的key进行hash分区 |
sortPartition | 指定字段对分区中的数据进行排序 |
map和mapPartition:
和Spark一样
map对每一个元素操作
mapPartition对每一个分区操作
flatMap:
用法和Spark中的一模一样,有一个新的用法 将DataSet中的每一个元素转换为另一个集合并压平为多个元素 将DataSet中的每一个元素转换为0~n个元素
Filter:
过滤
groupBy+reduce和groupBy+sum:
groupBy+reduce:分组+聚合
groupBy+sum:分组+聚合
groupBy+reduceGroup:
reduce:会将各个分区的数据拉取到主节点再聚合计算
reduceGroup:会将各个分区的数据先进行预聚合,再将结果发送给主节点
groupBy+reduceGroup = Spark中的reduceByKey
Aggregate:
Aggregate是聚合的底层API
Distinct:
去重
Join:
内连接
左外连接,左边的Dataset中的每一个元素,去连接右边的元素, 左边的全部保留,右边的满足条件的保留
fullOuterJoin:
全外连接,左右两边的元素,全部连接
Rebalance:
重平衡--按照轮询Round Robin进行平衡
kafka中如果消息没有key,那么这个消息发给各个分区就是使用轮询;如果消息有key默认就使用hash
类似于Spark中通过repartition解决数据倾斜
在Flink中可以使用rebalance来解决数据倾斜
分区:
partitionByHash按照hash值进行分区 partitionByRange按照数值范围进行分区 sortPartition对分区进行排序
minBy和min:
min:可以求最小,但不能保证其他列正确
minBy:可以求最小,也能够保证其他列正确
maxBy和max类似
2.3 批处理-Data Sinks
2.3.1 基于集合
基于本地集合的sink(Collection-based-sink)
可以sink到标准输出,error输出,collect()到本地集合
2.3.2 基于文件
基于文件的sink(File-based-sink)
可以sink到本地文件,hdfs文件(支持多种文件的存储格式,包括text文件,CSV文件等)
writeAsText():TextOuputFormat - 将元素作为字符串写入行。字符串是通过调用每个元素的toString()方法获得的。
3 流处理DataStream
3.1 流处理-DataSource
3.1.1 基于集合(参考批处理)
3.1.2 基于文件的(参考批处理)
3.1.3 基于Socket
3.1.4自定义source
3.2 流处理-Transformation
Transformation | Description |
Map | Takes one element and produces one element. A map function that doubles the values of the input stream: |
FlatMap | 采用一个数据元并生成零个,一个或多个数据元。将句子分割为单词的flatmap函数: |
Filter | 计算每个数据元的布尔函数,并保存函数返回true的数据元。过滤掉零值的过滤器: |
KeyBy | 逻辑上将流分区为不相交的分区。具有相同Keys的所有记录都分配给同一分区。在内部,keyBy()是使用散列分区实现的。指定键有不同的方法。 此转换返回KeyedStream,其中包括使用被Keys化状态所需的KeyedStream。 |
Reduce | 被Keys化数据流上的“滚动”Reduce。将当前数据元与最后一个Reduce的值组合并发出新值。 reduce函数,用于创建部分和的流: |
Fold | 具有初始值的被Keys化数据流上的“滚动”折叠。将当前数据元与最后折叠的值组合并发出新值。 折叠函数,当应用于序列(1,2,3,4,5)时,发出序列“start-1”,“start-1-2”,“start-1-2-3”,. .. val result: DataStream[String] = keyedStream.fold("start")((str, i) => { str + "-" + i }) |
Aggregations | 在被Keys化数据流上滚动聚合。min和minBy之间的差异是min返回最小值,而minBy返回该字段中具有最小值的数据元(max和maxBy相同)。 keyedStream.maxBy("key"); |
Window | 可以在已经分区的KeyedStream上定义Windows。Windows根据某些特征(例如,在最后5秒内到达的数据)对每个Keys中的数据进行分组。有关窗口的完整说明,请参见windows。 dataStream.keyBy(0).window(TumblingEventTimeWindows.of(Time.seconds(5))); |
WindowAll | Windows可以在常规DataStream上定义。Windows根据某些特征(例如,在最后5秒内到达的数据)对所有流事件进行分组。有关窗口的完整说明,请参见windows。 警告:在许多情况下,这是非并行转换。所有记录将收集在windowAll 算子的一个任务中。 dataStream.windowAll(TumblingEventTimeWindows.of(Time.seconds(5))); |
Window Apply | 将一般函数应用于整个窗口。下面是一个手动求和窗口数据元的函数。 注意:如果您正在使用windowAll转换,则需要使用AllWindowFunction。 |
Window Reduce | 将函数缩减函数应用于窗口并返回缩小的值。 |
Window Fold | 将函数折叠函数应用于窗口并返回折叠值。示例函数应用于序列(1,2,3,4,5)时,将序列折叠为字符串“start-1-2-3-4-5”: |
Windows上的聚合 | 聚合窗口的内容。min和minBy之间的差异是min返回最小值,而minBy返回该字段中具有最小值的数据元(max和maxBy相同)。 windowedStream.sum(0);windowedStream.sum("key");windowedStream.min(0);windowedStream.min("key");windowedStream.max(0);windowedStream.max("key");windowedStream.minBy(0);windowedStream.minBy("key");windowedStream.maxBy(0);windowedStream.maxBy("key"); |
Union | 两个或多个数据流的联合,创建包含来自所有流的所有数据元的新流。注意:如果将数据流与自身联合,则会在结果流中获取两次数据元。 dataStream.union(otherStream1, |
Window Join | 在给定Keys和公共窗口上连接两个数据流。
|
Interval Join | 在给定的时间间隔内使用公共Keys关联两个被Key化的数据流的两个数据元e1和e2,以便e1.timestamp + lowerBound <= e2.timestamp <= e1.timestamp + upperBound
|
Window CoGroup | 在给定Keys和公共窗口上对两个数据流进行Cogroup。
|
Connect | “连接”两个保存其类型的数据流。连接允许两个流之间的共享状态。 DataStream<Integer> |
CoMap,CoFlatMap | 类似于连接数据流上的map和flatMap |
Split | 根据某些标准将流拆分为两个或更多个流。 |
Select | 从拆分流中选择一个或多个流。 SplitStream<Integer> |
Iterate | 通过将一个 算子的输出重定向到某个先前的 算子,在流中创建“反馈”循环。这对于定义不断更新模型的算法特别有用。以下代码以流开头并连续应用迭代体。大于0的数据元将被发送回反馈通道,其余数据元将向下游转发。有关完整说明,请参阅迭代。 |
Extract Timestamps | 从记录中提取时间戳,以便使用使用事件时间语义的窗口。查看活动时间。 stream.assignTimestamps |
3.3 DataSink
3.3.1 基于集合(参考批处理)
3.3.2 基于文件(参考批处理)
3.3.3 自定义Sink-MySQL
3.3.4 自定义Sink-Kafka
4 Flink的Window操作
1.Flink支持两种划分窗口的方式(time和count)
如果根据时间划分窗口,那么它就是一个time-window,比如每1分钟统计一次或每10分钟统计一次最近xxx的数据
如果根据数据划分窗口,那么它就是一个count-window,比如每5个数据统计一次或每50个数据统计一次
2.Flink支持窗口的两个重要属性(窗口长度size和滑动间隔interval)
如果size=interval,那么就会形成tumbling-window(无重叠数据)--滑动的特例--滚动窗口
如果size>interval,那么就会形成sliding-window(有重叠数据)--正常的滑动窗口
如果size<interval,那么这种窗口将会丢失数据。比如每5秒钟,统计过去3秒的通过路口汽车的数据,将会漏掉2秒钟的数据。
3.通过组合可以得出四种基本窗口
time-tumbling-window 无重叠数据的时间窗口,设置方式举例:timeWindow(Time.seconds(5))---基于时间的滚动窗口
time-sliding-window 有重叠数据的时间窗口,设置方式举例:timeWindow(Time.seconds(10), Time.seconds(5))---基于时间的滑动窗口
count-tumbling-window无重叠数据的数量窗口,设置方式举例:countWindow(5)---基于数量的滚动窗口
count-sliding-window 有重叠数据的数量窗口,设置方式举例:countWindow(10,5)---基于数量的滑动窗口