巨量数据集合,指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。
大量化Volume 、 价值化Value 、 多样化Variety 、 真实性Veracity
CentOS7.2
ls 、ll、mkdir、cp、mv、rm、pwd、find、touch、rmdir、rm 、scp远程拷贝等
cat、more、head -n、less、tail(tail -n 行数 | tail -f )、vim、vi等
chown、chmod、useradd、grpadd等
chown 修改属主属组
chmod 修改权限 drwxr-xr-x 7 [4,2,1]
chmod [a|u|g|o]
[+|-][rwx] 文件名
、chown / chgroup
ps、kill、jps、du | df(磁盘使用情况 空间或者使用率)、top(动态查看系统运行情况,常用于应用故障监控)等
systemctl 动作 服务名
rpm | tar | yum
rpm: JDK
tar: Redis\Tomcat\Nginx
yum: mysql\git
tar解压安装 tar -zxf 文件 tomcat ,创建压缩 tar -czf 压缩文件名 需要压缩的文件
rpm安装 rpm -ivh rpm包
| rpm -e xxx
| rpm -qa |grep 安装包
-i | -U
yum安装:联网下载rpm包 ,解析rpm、依赖关系
编译安装:一般需要下载gcc-c++/gcc
ip a | ping | ifup | ifdown等
配置环境变量(家目录:home):~/.bashrc|.bash_profile 用户变量、/etc/profile
系统变量
网卡配置:/etc/sysconfig/network-script/ifcfg-ethx (基于Centos6)
去除服务自启动:chkconfig 服务名 off|on,查看所有系统服务:chkconfig --list
关闭防火墙:service iptables stop | chkconfig iptables off
查找看系统进程: ps -aux
文本内容查找:grep [-i] 搜索关键字 --color=always 搜索目标文件
查找指定进程:ps -aux | grep -i ‘sbin/sshd’ --color
只查询指定进程进程号:ps -aux | grep -i ‘sshd’ --color | grep sbin| awk ‘{print $2}’
杀死进程:kill -9 进程号 强制退出、kill -s TERM 进程号 优雅退出
查看端口:netstat -anp
查看磁盘容量: du -h /root/ #disk useage
df -h /root/ #disk free
50070:HDFSwebUI的端口号
8485:journalnode默认的端口号
9000:HDFS服务端口
8020:高可用访问数据rpc
8088:yarn的webUI的端口号
7077:spark基于standalone的提交任务的端口号
8081:worker的webUI的端口号
18080:historyServer的webUI的端口号
4040:application的webUI的端口号
2181:zookeeper的rpc端口号
9083:hive的metastore的端口号
60010:Hbase的webUI的端口号
6379:Redis的端口号
8080:sparkwebUI的端口号(master的webUI,Tomcat的端口号)
9092:kafka broker的端口
两个核心组件:MapReduce和Hadoop Distributed File System(HDFS)
HDFS:负责将海量数据进行分布式存储
MapReduce:负责提供对数据的计算结果的汇总
HDFS
:Hadoop Distribute File Sysytem
Map Reduce
:Hadoop 中的分布式计算框架 实现对海量数据并行分析和计算
HBase
:是一款基于列式存储的NOSql
Hive
:是一款sql解释引擎,可以将SQL语句翻译成MR 代码,并在集群中运行
Flume
:分布式日志收集系统
Kafka
: 消息队列 ,分布式的消息系统
Zookeeper
:分布式协调服务 用于 注册中心 配置中心 集群选举 状态监测 分布式锁
Hadoop版本2.9.2/2.6.0 Web UI端口:50070 操作端口:9000
擅长大数据集的可靠存储
基本Shell命令
hadoop fs -x * /* *代表文件 x代表shell命令
-put(上传) -get(下载)-ls(显示)-cp(复制) -mkdir(创建文件夹)
-moveFromLocal(从本地移动文件)-copyToLocal(从Hdfs复制到本地)
-rm -r -f(删除文件)-rmdir(删除文件夹)-cat(显示文件内容)
-tail -f(显示文件最新内容)-appendToFile(追加文件内容)
HDFS具有主/从体系结构。 HDFS群集由单个NameNode和管理文件系统名称空间并控制客户端对文件的访问的主服务器组成。此外,还有许多数据节点,通常是集群中每个节点一个,用于管理与它们所运行的节点相连的存储。 HDFS公开了文件系统名称空间,并允许用户数据存储在文件中。在内部,文件被分成一个或多个块,这些块存储在一组DataNode中。 NameNode执行文件系统名称空间操作,例如打开,关闭和重命名文件和目录。它还确定块到DataNode的映射。 DataNode负责处理来自文件系统客户端的读写请求。 DataNode还根据NameNode的指令执行块创建,删除和复制
发现绝大多数的Block块可用的时候,会自动退出安全模式。
NameNode和SecondaryNameNode关系?
伪分布式集群;
主要作用:用以合并NameNode产生的
editslog(写命令日志文件)+fsimage(内存快照文件)
,加快NameNode在启动时元数据恢复速度
HDFS不擅长小文件的存储?
- 小文件过多导致NameNode的内存浪费
- 不符合HDFS的设计原则,寻道时间大于IO读写时间
解决NameNode单点故障问题
namenode
:存储系统的元数据(用于描述数据的数据),例如 文件命名空间、block到DataNode的映射,负责管理DataNode
datanode
: 用于存储数据的节点 负责相应客户端读写请求 向NameNode 汇报块信息
block
:数据块 是对文件拆分的最小单元 表示 一个默认为128MB的切分 尺度,每个数据块副本,默认的副本因子为3,通过dfs.replication进行配置,另外用户还可以通过 dfs.blcoksize 设置块的大小
rack
:机架 使用机架对存储节点做物理编排 用于优化存储和计算
Zookeeper(配置维护、域名服务、分布式同步、组服务):一个分布式的、开放源码的分布式应用协调服务,是Hadoop和HBase的重要组件。
JournalNode:日志服务,主要用于主备NameNode元数据的同步;运行的JournalNode进程非常轻量,可以部署在其他的服务器上。注意:节点数至少3个
ZKFC:ZooKeeperFailoverController作为一个ZK集群的客户端,主要用来监控NameNode的状态信息
fsimage:元数据信息的备份,会被加载到内存中
editlog:Edits文件帮助记录文件增加和更新的操作 提高效率
在集群规模比较大时,机架感知能告诉集群哪台机器属于哪台机架。
Hadoop集群分辨某台lave机器属于哪个机架是需要Hadoop的管理者人为地告知Hadoop哪台机器属于哪个机架,会将这些机器与Rack的对应信息保存在内存中,用来作为对接下来所有的HDFS的写块操作分配datanode列表时的选择策略(尽量将多个副本分布在不同的Rack)。
机架感知需要考虑的情况:
不同节点的通信能够尽量发生在同一个机架之内
为了提高容错能力,namenode节点会尽可能把数据的副本放到多个机架上
写
读
Apache Hadoop YARN (Yet Another Resource Negotiator,另一种资源协调者)是一种新的 Hadoop 资源管理器,它是一个通用资源管理系统,可为上层应用提供统一的资源管理和调度,它的引入为集群在利用率、资源统一管理和数据共享等方面带来了巨大好处。
YARN的基本思想是将资源管理和作业调度/监视的功能拆分为单独的守护程序。这个想法是拥有一个全局ResourceManager(RM)和每个应用程序ApplicationMaster(AM)。应用程序可以是单个作业,也可以是作业的DAG。
ResourceManager具有两个主要组件:Scheduler和ApplicationsManager
NodeManager
:管理主机上计算资源,是每台机器的框架代理,向RM 汇报自身的状态信息。
ResourceManager
:负责集群计算资源的统筹规划,拥有着集群资源的最终决策权。
ApplicationMaster
:计算任务的Master,负责申请资源,协调计算任务。
YARNChild
:负责最实际的计算任务(MapTask|ReduceTask)
Container
:是计算资源的抽象,代表着一组内存、CPU、网络的占用,无论是ApplicationMaster和YARNChild都需要消耗一个Container。
由ZooKepper集群选举实现RM的主备服务切换;
InputFormat、Mapper、Partitioner(默认HashPartitioner)、Reducer、OutPutForMat
Combiner:用于优化MR程序,需要根据具体业务场景而定。
对与每一个MapTask的输出做局部汇总,以减少网络使用量
是MR程序中Mapper和Reducer之外的一种组件
父类就是Reducer
Combiner和Reducer 的区别在于运行位置
- Combiner 是在每一个Task所在的节点上运行
- Reducer 是在接收全局所有的Mapper的输出结果
适合累加 不适合求平均数
Combiner输出的KV 要与Reducer 的KV相对应
使用
化繁为简、分而治之
MapTask
映射将输入数据映射为KV结构
InputFormat
数据的输入格式
数据集剩余大小 > 128MB*1.1
RecordReader
MapTask
Shuffle
洗牌Map端Shuffle
Reduce端Shuffle
ReduceTask
计算ReduceTask
OutputFormat
首先通过程序员所编写的MR程序通过命令行提交
开始运行后称之为Job(一个MR程序就是Job),Job会向RM申请注册(会带一些信息)
如果RM注册通过,Job会从共享的文件系统中拷贝相关信息
Job紧着着会向RM提交完整的应用信息
a RM会计算出Job启动所需要的资源
b.连接到对应的NM启动一个MRAppMaster
MRAppMaster 在启动的时候会初始化Job
初始化Job 后,会去共享文件系统中回去切片
MRAppMaster 向RM 请求计算资源
连接到对应的NM ,消耗Container 启动YARNChild
获取完整的Job资源
计算
封装Job对象
设置读入写出的格式
设置写入写出的路径
设置计算逻辑
设置Map和Reduce的泛型
提交Job
由于某些key设计不合理,导致了数据负载不均衡的现象
参考资料:https://blog.csdn.net/qq_43193797/article/details/86005144
implements Writable
版本1.2.4 WebUI http://hostname:16010
解决了大规模结构化数据的存储
是一个基于Google BigTable论文设计的高可靠性、高性能、可伸缩的分布式存储系统
HDFS只能存储大文本文件,最多算是文件系统,文件系统对数据的管理粒度比较粗糙,无法完成单条记录级别的数据CRUD,因此当需要对海量数据进行随机读写的时候,HDFS就无能为力。因此HBase作为构建在HDFS之上一款NoSQL数据服务,可实现对海量数据集高效的随机读写。
数据库类型:HBase中的数据类型都是字符串类型(string)
数据操作:HBase只有普通的增删改查等操作,没有表之间的关联查询
存储模式:HBase是基于列式存储模式,而RDBMS是基于行式存储的
应用场景:HBase适合存储大量数据,查询效率极高
略
https://www.cnblogs.com/frankdeng/p/9310278.html
HBase通过zk来做Master的高可用,RegionServer的监控、元数据的入口以及集群配置的维护工作
为 RegionServer 分配 Region
负责 RegionServer 的负载均衡,发现失效的 RegionServer 并重新分配其上的 Region
发现失效的Region,将失效的Region分配到正常的RegionServer上,当RegionServer失效时,协调对应的HLog的拆分
数据大小>=N^2*128MB
(N为Region数量),当N=9时,切分大小会超过10GB,此时就按照10GB进行切分。memStore 是放在内存里的。保存修改的数据即keyValues。当memStore的大小达到一个阀值(默认128MB)时,memStore会被flush到文 件,即生成一个快照。目前hbase 会有一个线程来负责memStore的flush操作。
Flush机制
- MemStore级别:当Region中人一个MemStore的大小达到上限(默认为128MB ),会触发MemStore刷新
- Region级别:当Region中所有的MemStore大小总和达到的上限(默认256M)
- RegionServer级别:当RegionServer中所有的MemStore大小总和达到上限(默认 40%的JVM内存使用量),会触发部分Memstore刷新。Flush顺序是按照Memstore由大到小执行,先Flush Memstore最大的Region,再执行次大的,直至总体Memstore内存使用量低于阈值(默认 38%的JVM内存使用量)。
- 当一个RegionServer中HLog数量达到了上限(可通过参数hbase.regionserver.maxlogs配置)时,系统会选取最早的一个 HLog对应的一个或多个Region进行flush
- HBase定期刷新Memstore:默认周期为1小时,确保Memstore不会长时间没有持久化。为避免所有的MemStore在同一时间都进行flush导致的问题,定期的flush操作有20000左右的随机延时。
- 手动执行flush:用户可以通过shell命令 flush ‘tablename’或者flush ‘region name’分别对一个表或者一个Region进行flush。
HFile(StoreFile) 用于存储HBase的数据(Cell/KeyValue)。在HFile中的数据是按RowKey、Column Family、 Column排序,对相同的Cell(即这三个值都一样),则按timestamp倒序排列。
由于MemStore中存储的Cell遵循相同的排列顺序,因而Flush过程是顺序写,由于不需要不停的移动磁盘指针,因此磁盘的顺序写性能很高。
https://blog.csdn.net/qq_38180223/article/details/80864778
长度:建议在16个字节以内
唯一:唯一不重复(加盐、预分区、反转、哈希)
散列:尽量分配均匀
用户标识:时间:随机盐
写过程
Client访问ZK,根据ROOT表获取meta表所在Region的位置信息,并将该位置信息写入Client Cache。(注:为了加快数据访问速度,我们将元数据、Region位置等信息缓存在Client Cache中。)
Client读取meta表,再根据meta表中查询得到的Namespace、表名和RowKey等相关信息,获取将要写入Region的位置信息(此过程即Region三层定位,如下图),最后client端会将meta表写入Client Cache。
Client向上一步HRegionServer发出写请求,HRegionServer先将操作和数据写入HLog(预写日志,Write Ahead Log,WAL),再将数据写入MemStore,并保持有序。(联想:HDFS中也是如此,EditLog写入时机也是在真实读写之前发生)
当MemStore的数据量超过阈值时,将数据溢写磁盘,生成一个StoreFile文件。
当Store中StoreFile的数量超过阈值时,将若干小StoreFile合并(Compact)为一个大StoreFile。
当Region中最大Store的大小超过阈值时,Region分裂(Split),等分成两个子Region。
读过程
目的:减少StoreFile数量,提升数据读取效率。
Compaction分为两种:
major compaction
将Store下面所有StoreFile合并为一个StoreFile,此操作会删除其他版本的数据(不同时间戳的)
minor compaction
选取Store下的部分StoreFile,将它们合并为一个StoreFile,此操作不会删除其他版本数据。
1.9.0
Agent
:代表一个Flume服务实例,多个Agent构成一个服务集群
Source
:接受获取采集数据源数据,将数据封装为Event事件对象
Channel
:事件队列(缓冲区),主要负责数据临时存储
Sink
:将Channel中数据输出保存到指定存储位置;
Interceptor
: 拦截器,对Source拉取的数据进行预处理
ChannelSelector
:通道选择器,主要选择通道,分发和复制
Flume的Selector的形式有2种:
①Replication 形式 数据同步给多个Channel
②Multiplexing形式 数据分流
SinkGroup
:负载均衡或者容错
Source
:exec、spooldir(读取并添加后缀)、avro(一次性)、netcat(Tcp协议)、taidir、kafka等
TAILDIR:读取文件完成后,会接续读取此文件,如果有最新的文件内容会对此文件的新的内容进行读取在/root/.flume/taildir_position.json文件记录着当前文件夹每一个文件的读取的偏移量(pos),所以在检测到当前文件和其它的文件的内容有更新时,会从上次记录的偏移量的最后一个加1进行读取
Channel
:memory、file、jdbc、kakfa等
Sink
:HDFS、HBase、Kafka、File Roll、Avro Sink、Logger等
Interceptor
: UUID、HOST、Timestamp、Regex_filter、Static等
为什么使用Kafka
参考: https://blog.csdn.net/qq_38019655/article/details/84112210
版本2.11-0.11.0.0
FIFO 先进先出
系统解耦和削峰填谷
消息的是系统间通信的载体,是分布式应用不可获缺的一部分。目前系统间发送消息有两种种类。
即使消息:打电话、表达提交、WebService、Dubbo|SpringCloud
要求消息发送方和接受放必须同时在线,一般都需要和接收方建立会话。
发送方不理会对方是否在线,一般不需要和接收方建立会话,在接受方上线后,一般会获取发送方发送的消息。
显而易见,可以看出消息队列就是使用的异步消息的模型。
同组负载均衡,不同组广播
earliest
:如果当前消费组没有已提交的offset,则从分区队列头部拉取消息latest
(默认):如果当前消费组没有已提交的offset,则从分区队列的尾部拉取消息缓冲区大小 | 逗留时间
)时以批量的形式发送存放kafka集群;数据写入到kafka集群时,kafka服务会给生产者返回应答信息;
幂等:相同数据多次写入只会保留一个结果
只能保证单个Producer对于同一个
的Exactly Once语义
props.put("enable.idempotence",true);
只支持两种事务格式级别:read_uncommited(默认)
、read_commited
保证Producer对于topic所有分区的Exactly Once语义
消费和生产在同一个事务环境中;
https://www.cnblogs.com/yoke/p/11486196.html
https://blog.csdn.net/lukabruce/article/details/101012815
略
rocksdb
+ 远程状态 changlog topic
)xxx-reparation topic
)版本:3.4.6 端口:2181
Apache ZooKeeper致力于开发和维护可实现高度可靠的分布式协调的开源服务器。
是Google的Chubby的开源实现。
对于任何的分布式系统,不能同时满足一致性、可用性、分区容错性,只能够同时满足其中两个特性;
ZooKeeper CP系统(强一致性),不论访问ZooKeeper哪一个节点都可以获取到相同的数据;
ZK提供的命名空间与标准文件系统非常相似,一个名称由斜线分割开来的路径的进行表示,ZK 中每一个节点都通过路径来表示。
ZK 通过像树一样的结构来进行维护,并且每个节点通过路径来访问和标示。而且每个节点上还存储着相应的数据,这个数据不仅包含本身用户存储的数据,还有数据长度,数据创建时间,数据修改时间。
维护树形层次空间,每一个节点称为ZNode
,类型:
Zookeeper原生API支持通过注册Watcher 来实现事件监听,但是Watcher 通知机制是一次性的。因此如果使用原生的API去实现Watcher 机制,需要反复注册,比较繁琐。
观察或者监控ZooKeeper节点信息的改变
https://blog.csdn.net/u013679744/article/details/79222103
https://www.jianshu.com/p/2bceacd60b8a
可以将结构化的数据文件映射为一张数据库表,并提供简单的sql查询功能,可以将sql语句转换为MapReduce任务进行运行。
select
all[distinct] 字段列表...
from 表名
where 过滤条件
group by 分组字段
having 筛选条件
order by 排序字段 asc[desc] # 全局排序 1个reduceTask
[sort by 排序字段] # 分区内排序
[partition by 分区字段] # 分区时所使用的key
[cluster by 字段] # 排序字段和分区字段一致
limit n
ETL,是英文Extract-Transform-Load的缩写,用来描述将数据从来源端经过抽取(extract)、转换(transform)、加载(load)至目的端的过程。ETL一词较常用在数据仓库,但其对象并不限于数据仓库。
https://baike.baidu.com/item/ETL/1251949?fr=aladdin
参阅专业论文
https://blog.csdn.net/qq_36174081/article/details/89945050?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
专门为计算而生的语言,Scala将(Java后者C++)
面向对象设计
和函数式编程
结合在一起的简洁的高级编程语言。
Java编程语言
平台无关性 平台无关性是指Java能运行于不同的平台
安全性 Java的编程类似C++,学习过C++的读者将很快掌握Java的精髓
面向对象 Java吸取了C++面向对象的概念
分布式 Java建立在扩展TCP/IP网络平台上
健壮性 Java致力于检查程序在编译和运行时的错误
类型转换
通俗理解: 自动类型提升
规则:
Byte ---> Short ---> Int ---> Long ---> Float ---> Double
char
值类型可以按照下面的方向进行转换:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MI6AQxDU-1586439283542)(C:\Users\战神\AppData\Roaming\Typora\typora-user-images\1584448676269.png)]
Scala类型层次结构【重点】
在Scala中,所有的值都有类型,包括数值和函数。下图阐述了类型层次结构的一个子集。
Any
是所有类型的超类型,也称为顶级类型。它定义了一些通用的方法如equals
、hashCode
和toString
。Any
有两个直接子类:AnyVal
和AnyRef
。AnyVal
代表值类型。有9个预定义的非空的值类型分别是:Double
、Float
、Long
、Int
、Short
、Byte
、Char
、Unit
和Boolean
。Unit
(类似于java中的void
)是不带任何意义的值类型,它仅有一个实例可以像这样声明:()
。所有的函数必须有返回,所以说有时候Unit
也是有用的返回类型。AnyRef
代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef
的子类型。如果Scala被应用在Java的运行环境中,AnyRef
相当于java.lang.Object
。Nothing
是所有类型的子类型,也称为底部类型。没有一个值是Nothing
类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。Null
是所有引用类型的子类型(即AnyRef
的任意子类型)。它有一个单例值由关键字null
所定义。Null
主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。我们将在后面的章节中介绍null
的替代方案。break
Java break: 表示结束循环
注意:scala中没有
break
和continue
关键字
yield
for 循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合
首先会将
yield
表达式的值会被加入存放到一个缓冲区中在遍历结束之后,会返回一个基于缓冲区的新集合,类型为
Vector
新集合的元素的类型和遍历集合的类型是一致的
函数
柯里化函数:接受单个参数并且返回一个函数对象的过程
函数对象:function0~function22
闭包:函数引用到了一个它作用域外的变量,称为函数的闭包;
数组
对象和类
apply 方法是object类型中的一个方法,通常作为工厂方法创建伴生类的对象【简化方式】
结论:** 调用的伴生对象中的apply方法,用以创建伴生类**
unapply: 伴生对象中的方法,作用和apply方法相反,接受一个伴生类对象,返回伴生类对象的成员
@BeanProperty
此注解加载成员的前面,scala编译时,会自动生产java版本和scala版本的getter/setter方法
类似于java重载的构造方法
Java中Override(覆盖)和Overload(重载)的区别?
Override:
(1)返回值类型、方法名、形参列表和父类相同
(2)访问修饰符与父类相同或者更宽
Overload
(1)方法名相同
(2)形参类表不同
(3)与返回值类型和修饰符无关
注意:scala的一个类中,可以有一个主构造器和若干个辅助构造器
特质
特质: 类似于Java中的接口
特质关键词trait
特质可以同时用于抽象方法和具体方法
无父类 实现特质extends trait1 with trait2 with trait3 ...
有父类 实现特质extends 父类 with 特质1 with 特质2 ...
异常处理
类似于Java中的异常处理
集合
Scala 集合分为可变的和不可变的集合:
- 可变集合(
mutable
) 可以在适当的地方被更新或扩展。- 不可变集合(默认
immutable
)类永远不会改变。
import scala.collection.JavaConverters._ //隐式增强
隐式转换
在编译不通过时,自动寻找隐式转换尝试将一种类型转换为另外一种类型
常用使用方式:
Scala会考虑如下位置的隐式转换函数:
隐式转换在如下三种不同情况下会被考虑:【知道】
有三种情况编译器不会尝试使用隐式转换: 【知道】
模式匹配
高阶函数(重点)
scala的函数实现wordcount
val conf = new SparkConf().setMaster("local[*]").setAppName("transformation test")
val sc = new SparkContext(conf)
sc
.makeRDD(List("Hello Spark", "Hello Scala", "Hello Hello Spark", "Scala very good"))
.flatMap(_.split(" "))
.map((_, 1))
.groupByKey()
.map(t2 => (t2._1, t2._2.size))
.foreachPartition(itar => { // 对RDD的每一个分区(Task, 保证Task共享一个连接对象)应用迭代操作
// itar代表的是每一个分区所有元素的迭代器
classOf[com.mysql.jdbc.Driver]
val connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "1234")
val pstm = connection.prepareStatement("insert into t_word values(?,?)")
itar.foreach(t2 => {
pstm.setString(1, t2._1)
pstm.setInt(2, t2._2)
pstm.executeUpdate()
})
pstm.close()
connection.close()
})
泛型
sealed、var、val、lazy区别?
lazy:在scala中变量或者常量可以被声明为懒加载的值,值在第一次使用时,才会进行初始化
sealed(密封类)
所有子类都必须在与该密封类相同的文件中定义,主要作用:
- 防止滥用继承:sealed关键字可以修饰类和特质。密封类提供了一种约束:**不能在类定义的文件之外定义任何新的子类。如:scala的List的实现使用了sealed关键字
语法:
[T:M]
上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的隐式值
版本2.4.4 Web UI:8080 独立模式端口号(服务端口):7077
Windows操作系统配置主机名映射
C:\Windows\System32\drivers\etc\HOSTS
快如闪电:
统一:
计算引擎:
特点
分析问题(MapReduce)
Spark自带资源管理调度系统(Standalone)
工作步骤:
第一步:在提交Spark应用时会初始化Driver(JVM进程),并且创建SparkContext
第二步:Driver会根据任务需要的资源信息,请求资源管理器(ClusterManager)分配计算资源
第三步:ClusterManager会将分配的计算资源反向注册给Driver
第四步:Driver端的DAGScheduler划分Stage,将一个复杂的计算任务肢解为若个小的任务,每一个Stage阶段都包含一个TaskSet
第五步:Driver端的TaskScheduler会根据阶段的划分,逐一提交Stage的TaskSet,运行在预支的计算节点。Spark计算节点在运行任务时,TaskSet中的每一个Task运行在Thread线程中,进行分布式并行计算。
第六步:当所有阶段的任务运行结束后,通过SparkContext释放占用的计算资源,通知ClusterManager回收
RDD:弹性分布式数据集对象,是Spark中最为核心的抽象,主要对各种数据源统一抽象,不可变、可分区、容错、支持并行处理计算数据集合;
lazy
,将一个RDD转换为另外一个RDD
结论:
WidthDependency
):父RDD的一个分区指向了多个子RDD的分区;(一对多)总结如下:
流程: ① 提交任务 —> ②划分阶段 —> ③ 封装TaskSet —> ④ 提交TaskSet
# 第一步:
SparkContext # runJob // spark应用的运行入口
dagScheduler.runJob
# 第二步:
DAGScheduler # runJob
submitJob // 提交任务
# 第三步:
DAGScheduler # submitJob
eventProcessLoop # post(JobSubmitted) // 将提交任务的事件 存放到事件处理器中
# 第四步:
DAGSchedulerEventProcessLoop # onReceive(Event) // DAG调用器接受一个事件,并对事件进行处理
doOnReceive(event)
# 第五步:
DAGSchedulerEventProcessLoop # doOnReceive(event)
// scala样例类的模式匹配 匹配到第一个case语句
case JobSubmitted => dagScheduler.handleJobSubmitted() // 处理任务提交的事件
# 第六步:
DAGScheduler # handleJobSubmitted
var finalStage: ResultStage = null // 最终(最后一个)Stage
// 通过finalRDD(最后一个RDD)创建ResultStage
finalStage = createResultStage(finalRDD, func, partitions, jobId, callSite)
// 最后一行 提交最后一个阶段
submitStage(finalStage)
# 第七步:
DAGScheduler # submitStage(finalStage)
if (!waitingStages(stage) && !runningStages(stage) && !failedStages(stage)) {
val missing = getMissingParentStages(stage).sortBy(_.id) // 获取当前stage的父Stage【重点,宽窄依赖】
logDebug("missing: " + missing)
if (missing.isEmpty) { // 判断父stage是否为空,如果为空,表示到血统最顶端,则开始进行阶段提交
logInfo("Submitting " + stage + " (" + stage.rdd + "), which has no missing parents")
submitMissingTasks(stage, jobId.get) // 提交当前阶段的TaskSet
} else { // 判断父stage是否为空,如果不为空,则通过递归方式,继续寻找父Stage
for (parent <- missing) {
submitStage(parent)
}
waitingStages += stage
}
}
# getMissingParentStages(stage) // 根据依赖类型不同,划分stage
// 如果是 ShuffleDependency【宽依赖】 则创建ShuffleMapStage划分阶段
// 如果是 NarrrowDependency【窄依赖】 则将窄依赖的RDD存放到stack,等待后续访问
for (dep <- rdd.dependencies) {
dep match {
case shufDep: ShuffleDependency[_, _, _] =>
val mapStage = getOrCreateShuffleMapStage(shufDep, stage.firstJobId)
if (!mapStage.isAvailable) {
missing += mapStage
}
case narrowDep: NarrowDependency[_] =>
waitingForVisit.push(narrowDep.rdd)
}
}
# 第八步:
DAGScheduler # submitMissingTasks(stage, jobId.get) // 通过DAGScheduler逐一提交划分好的阶段
val tasks: Seq[Task[_]] = try {
val serializedTaskMetrics = closureSerializer.serialize(stage.latestInfo.taskMetrics).array()
stage match {
case stage: ShuffleMapStage => // 根据stage类型不同,封装不同的Task实例,并且Task数量由当前stage的分区数量决定
stage.pendingPartitions.clear()
partitionsToCompute.map { id =>
val locs = taskIdToLocations(id)
val part = partitions(id)
stage.pendingPartitions += id
new ShuffleMapTask(stage.id, stage.latestInfo.attemptNumber,
taskBinary, part, locs, properties, serializedTaskMetrics, Option(jobId),
Option(sc.applicationId), sc.applicationAttemptId, stage.rdd.isBarrier())
}
case stage: ResultStage =>
partitionsToCompute.map { id =>
val p: Int = stage.partitions(id)
val part = partitions(p)
val locs = taskIdToLocations(id)
new ResultTask(stage.id, stage.latestInfo.attemptNumber,
taskBinary, part, locs, id, properties, serializedTaskMetrics,
Option(jobId), Option(sc.applicationId), sc.applicationAttemptId,
stage.rdd.isBarrier())
}
}
}
// 首先封装了TaskSet(tasks.toArray) 将上面的Seq[Task]转为定长数组 封装到TaskSet中
// taskScheduler.submitTasks(taskSet)
taskScheduler.submitTasks(new TaskSet(
tasks.toArray, stage.id, stage.latestInfo.attemptNumber, jobId, properties))
Spark的计算核心是抽象了一个RDD,RDD存在分区和依赖关系。根据RDD的分区和子RDD的分区的 映射关系将RDD依赖分为宽依赖和窄依赖(ShuffleDependency | NarrowDependency)。系统在任务提交的时候会调用DAGScheduler计算任务的state方法。首先会根据FinalRDD(最终RDD)创建一个ResultStage,然后调用DAGScheduler#submitStage(state)方法,在Stage提交阶段,系统在提交当前Stage时,需要逆向反推出当前ResultStage的所有父Stage(ShuffleMapStage),在寻找父Stage的时候,系统会根据RDD的宽窄依赖做Stage划分。如果当前Stage没有父Stage的时候,系统会将当前Stage所对应的任务提交;如果是ShuffleMapStage,系统会根据分区计算出ShuffleMapTask,并且将该Task封装成TaskSet提交;如果是ResultStage,系统会根据分区计算出ResultTask,并且将该Task封装成TaskSet提交。
cache() :MEMORY_ONLY
persistent()
StorageLevel.MEMORY_ONLY # 直接将RDD只存储到内存,效率高,占用空间大
StorageLevel.MEMORY_ONLY_2 # 直接将RDD只存储到内存,效率高,占用空间大,并且存储两份
StorageLevel.MEMORY_ONLY_SER # 将RDD先进行序列化,效率相对较低,占用空间稍微小
StorageLevel.MEMORY_ONLY_SER_2 # 将RDD先进行序列化,效率相对较低,占用空间稍微小,并且存储 两份
StorageLevel.MEMORY_AND_DISK
StorageLevel.MEMORY_AND_DISK_2
StorageLevel.MEMORY_AND_DISK_SER
StorageLevel.MEMORY_AND_DISK_SER_2 # 不确定情况下,一般使用该种缓存策略
StorageLevel.DISK_ONLY # 基于磁盘存储
StorageLevel.DISK_ONLY_2
StorageLevel.DISK_ONLY_SER
StorageLevel.DISK_ONLY_SER_2
重新计算(默认):从头再次计算一遍
缓存:可以通过RDD缓存恢复某一个阶段的结算数据;
检查点(checkpoint):某一个阶段计算状态持久化保存到磁盘(HDFS)中;链路长的推荐使用
当算子函数使用到一个外部变量时,会自动将外部变量序列化并且拷贝到每一个Task任务中;
广播变量(优化策略)
累加器
https://blog.csdn.net/lukabruce/article/details/81504220
Shuffle调优
collect和foreach区别
Reduce和ReduceByKey
sample转换、takeSample动作
统计系统访问PV(page view)
**系统UV(Unique view) **
Spark Streaming是Spark Core扩展(RDD),可以对实时流数据进行可靠、高吞吐、容错的流数据处理。
① 构建数据源: Spark Streaming在计算时,输入数据(数据源Sources)可以有多种类型,如:Kafka【重点】、Flume、TCP;
② 通过Streaming中提供高阶函数,如map、reduceByKey、join、window等,对流数据进行处理
③ 将流数据处理结果写出到存储系统(FS、DB)或者仪表盘(数据展示系统)
离散数据流,是Spark Streaming 中核心抽象,建立Spark RDD微批基础之上,代表是连续的数据流。
注意DStreaming不支持延迟数据,水位线、EventTime处理,窗口长度和滑动间隔必须微批的间隔整数倍
批数据&流数据
状态管理mapWtihState(增量更新)
和updateStateWithKey(全量更新)
状态数据的恢复StreamingCotnext.getOrCreate(checkpointpath)
检查点数据
只支持处理时间,不支持事件时间
对批次数据或者结构化流数据的处理工具;
类似于Hive,数据仓库(Data WareHourse)工具,简化Spark应用开发
提供了两种交互方式:1) SQL 脚本 ,2) Dataset API (strong-typed类型、untyped类型操作)
DataSet分布式数据集对象,增强版本的RDD,通过RDD隐式转换获得DS
DataFrame分布式数据帧对象,特殊的DataSet[Row]
DataSet
Dataset支持无类型操作,用户无需获取操作的类型,操作仅仅是列名
DataFrame
是一个命名列的数据集,用户可以直接操作column 因此几乎所有Dataframe推荐操作都是 无类型操作
strong typed)和无类型(
untyped`)操作推荐DF无类型操作
【重点】
(表连接)Over 窗口函数(重点
TopN):求每个分类商品热卖榜、时间排序
窗口(开窗)函数
作用: 窗口函数使用over,对一组数据进行操作,返回普通列和聚合列
DB 聚合查询:
sum count avg
返回一行结果
窗口函数分为3大类:
语法:
窗口函数名() over([partition by 分区字段] order by 字段 asc | desc [range | rows between unbounded preceding and unbounded following])
val df2 = List(
(1, "zs", true, 1, 15000),
(2, "ls", false, 2, 18000),
(3, "ww", false, 2, 14000),
(4, "zl", false, 1, 18000),
(5, "win7", false, 1, 16000))
.toDF("id", "name", "sex", "dept", "salary")
//************************************************************************************
# 使用窗口函数 每一个用户新增聚合列 代表的当前用户所在部门的员工的最高工资
1 "zs" true 1 15000 18000
2 "ls" false 2 18000 18000
2 "ww" false 2 14000 18000
select
id,name,sex,dept,salary,
max(salary) over(partition by dept order by salary desc rows between unbounded preceding and unbounded following) as dept_max_salary
from
t_user
//************************************************************************************
val df2 = List(
(1, "zs", true, 1, 15000),
(2, "ls", false, 2, 17000),
(3, "ww", false, 2, 14000),
(4, "zl", false, 1, 18000),
(6, "zl2", false, 1, 18000),
(5, "win7", false, 1, 16000))
.toDF("id", "name", "sex", "dept", "salary")
df2.createOrReplaceTempView("t_user")
// 窗口函数1: 当前行为基准在 分区内数据可视范围 unbounded preceding(-2^63) and unbounded following (2^63-1)
// 窗口函数2: 当前行为基准在 分区内数据可视范围 rowsBetween(start = -1,end = 1) 1 preceding and 1 following
// 窗口函数3: 分区内数据可视范围 rowsBetween(start = -1,end = 0) 当前行 + 上一行
// 窗口函数4: 分区内数据可视范围 rowsBetween(start = 0,end = 1) 当前行 + 下一行
// 窗口函数5: 分区内数据可视范围 rangeBetween(start,end) 通过排序字段 动态计算范围区间[基准行的排序字段的值 - start,基准行的排序字段的值 + end]
// 窗口函数6: 排行窗口函数(非连续排名)
// 窗口函数7: 排行窗口函数(连续排名)
// 窗口函数8: lead(列名,n) over(partition by ... order by ...)-- 取出基准行后n行数据作为聚合列的结果。
// 窗口函数9: lag(列名,n) over(partition by ... order by ...)-- 取出基准行前n行数据作为聚合列的结果。
spark
.sql(
"""
|select
| id,name,sex,dept,salary,
| max(salary) over(partition by dept order by salary desc rows between unbounded preceding and unbounded following) as dept_max_salary,
| max(salary) over(partition by dept order by salary desc rows between 1 preceding and 1 following) as dept_max_salary2,
| max(salary) over(partition by dept order by salary desc rows between 1 preceding and 0 following) as dept_max_salary3,
| max(salary) over(partition by dept order by salary desc rows between 0 preceding and 1 following) as dept_max_salary4,
| max(salary) over(partition by dept order by salary desc range between 2000 preceding and 2000 following) as dept_max_salary5,
| rank() over(partition by dept order by salary desc) as rank1,
| dense_rank() over(partition by dept order by salary desc) as rank2,
| lead(salary,2) over(partition by dept order by salary desc) as next,
| lag(salary,1) over(partition by dept order by salary desc) as before
|from
| t_user
""".stripMargin)
.show()
//------------------------------------------------------------------------------------
略
建议在工作中使用DataFrame的纯SQL操作(无类型的)
给dataFrame起别名(视图名)
建立在Spark SQL基础之上的一个用于进行流数据处理的引擎
流数据进行处理,处理容错语义:
- at exactly once: 精确一次; 流数据不论处理成功还是失败 一定能够精确处理1次
- at least once:最少一次; 流数据处理成功(1次),处理失败(n次)
- at most once: 最多一次;流数据处理成功(1次),处理失败(0次)
优点:
select word,num(word) from t_word group by word
at exactly once
, Spark 2.3版本之后,提供端对端低于1ms的处理延迟和at least once
核心思想将流数据视为一个持续追加的数据库表; Spark结构流处理类似于Spark SQL的批处理操作
InputTable:输入表,代表反映的是流数据
ResultTable(结果表,也是状态表):对输入表应用query结果表
OutputMode:
聚合操作:Complete/ Update 无聚合操作:Append/Update
注意:
Event Time 语义处理
提供了基于EventTime语义的处理,在使用的时候需要用户指定时间戳字段。
val wordCounts=df.select("value").as[String].map(_.split(","))
.map(tokens=>(tokens(0),new Timestamp(tokens(1).toLong)))
.toDF("word","timestamp")
.groupBy(
window($"timestamp","4 seconds","2 seconds"),
$"word"
)
Spark结构化流实现端对端精确一次处理语义;
输入端: 通常是Kafka,处理引擎正常处理流数据则提交消费位置offset;如果未正常处理流数据则不提交消费位置offset,下一次消费数据时,还会重新拉取这条记录;
输出端: 实现幂等写操作(写出一次或者多次影响结果是一致的)
延迟数据:Late Data,先生产后抵达或者数据乱序问题; 处理方案:参与计算、丢弃(Flink 旁路输出)
水位线:
wm = max event time - late threshold
,版本:1.8.1 WebUI:8081
Apache Flink 是一个框架和分布式处理引擎,用于在无边界和有边界数据流上进行有状态的计算。Flink 能在所有常见集群环境中运行,并能以内存速度和任意规模进行计算。
第一代大数据处理方案:2006年Hadoop的MapReduce-批/HDFS, 2014年9月份 apache Storm-流
第二代大数据处理方案:2014年2月 Spark RDD -批处理 ,DStream - 流 (批模拟流 )延迟高
第三代大数据处理方案:2014年12月 Flink DataStream-流,Dataset- 批 吞吐量高,低延迟特点。
Flink和Spark相似采用先进的DAG模型做任务拆分完成数据的内存计算,但是Flink是一个纯流式
计算引擎。不同于Spark在批处理之上构建流处理,Flink设计恰恰和Spark相反,Flink是在流计算上构建批处理
。
https://ci.apache.org/projects/flink/flink-docs-release-1.10/zh/concepts/runtime.html
JobManagers:称为Master,负责分布式任务调度,调度Task执行,协调checkpoint,实现故障恢复。
TaskManagers :称为Worker节点,负责执行Dataflow Graph(DAG)中SubTask,负责数据缓冲和交换。主动连接JobManager,声明自身状态信息和汇报应经分配的任务。
client :并不是运行时一个部分(和Spark Driver不同),负责生成或者发送dataflow给JobManager。
“Oprator Chain”:将多个操作符合并到一个Task中,减少不必要的任务间网络通信。
“Task”:类似于Spark中Stage,将任务拆分成若干个阶段。
“SubTaks”:每个Task都有任务执行并行度,每个Task根据并行度拆分成若干个SubTask(等价于线程)
当前的flink应用由3个Task,5个SubTask构成,每一个SubTask会由1个Thread处理
注:
Flink中的Task等价于Spark中的Stage
Flink根据Operator Chain划分任务Task,两种依据:Forward和Hash | Rebalance
每个 worker(TaskManager)都是一个 JVM 进程,并且可以在不同的线程中执行一个或多个 subtasks。为了控制 worker 接收 task 的数量,worker 拥有所谓的 task slots (至少一个)。
**每个 task slots 代表 TaskManager 的一份固定资源子集。**例如,具有三个 slots 的 TaskManager 会将其管理的内存资源分成三等份给每个 slot。 划分资源意味着 subtask 之间不会竞争资源,但是也意味着它们只拥有固定的资源。注意这里并没有 CPU 隔离,当前 slots 之间只是划分任务的内存资源。
**通过调整 slot 的数量,用户可以决定 subtasks 的隔离方式。**每个 TaskManager 有一个 slot 意味着每组 task 在一个单独的 JVM 中运行(例如,在一个单独的容器中启动)。拥有多个 slots 意味着多个 subtasks 共享同一个 JVM。 Tasks 在同一个 JVM 中共享 TCP 连接(通过多路复用技术)和心跳信息(heartbeat messages)。它们还可能共享数据集和数据结构,从而降低每个 task 的开销。
TaskSlot会对计算节点内存进行均分,不同的Job持有不同TaskSlot,继而程序在运行时实现内存隔离。任意job在执行之前都必须分配额定数据的TaskSlot,这些TaskSlot和该job中最大的Task并行度相等。
不同Job间通过TaskSlot进行隔离
同一个Job的不同Task的SubTask之间可以共享slot
同一个Job的相同Task的SubTask之间不可以共享slot
默认情况下,Flink 允许 subtasks 共享 slots,即使它们是不同 tasks 的 subtasks,只要它们来自同一个 job。因此,一个 slot 可能会负责这个 job 的整个管道(pipeline)。允许 slot sharing 有两个好处:
APIs 还包含了 resource group 机制,它可以用来防止不必要的 slot sharing。
根据经验,合理的 slots 数量应该和 CPU 核数相同。在使用超线程(hyper-threading)时,每个 slot 将会占用 2 个或更多的硬件线程上下文(hardware thread contexts)。
slot 是指 taskmanager 的并发执行能力;
如上图所示:taskmanager.numberOfTaskSlots:3;即每一个 taskmanager 中的分配 3 个 TaskSlot, 3 个 taskmanager 一共有 9 个 TaskSlot。
parallelism 是指 taskmanager 实际使用的并发能力
如上图所示:parallelism.default:1;即运行程序默认的并行度为 1,9 个 TaskSlot 只用了 1 个,有 8 个空闲。设置合适的并行度才能提高效率。
parallelism 是可配置、可指定的;
上图中 example2 每个算子设置的并行度是 2, example3 每个算子设置的并行度是 9。
分析:
详细剖析:
https://ci.apache.org/projects/flink/flink-docs-release-1.10/zh/ops/deployment/yarn_setup.html
有状态操作或者操作算子在处理DataStream的元素或者事件的时候需要存储计算的中间状态,这就使得状态在整个Flink的精细化计算中有着非常重要的地位
如:
- 记录保存某个时间节点到当前时间节点的状态数据
- 在每分钟/小时/天汇总事件时,状态将保留待处理的汇总。
- 在数据点流上训练机器学习模型时,状态保持模型参数的当前版本。
- 当需要管理历史数据时,状态允许有效访问过去发生的事件。
在学习使用flink时,需要掌握了解状态,以便使用检查点状态容错并设置流应用保存点;flink同样支持多种状态备份,如:内存、文件系统、RocksDB等
通常和 key 相关,仅可使用在
KeyedStream
的方法和算子中。
你可以把 Keyed State 看作分区或者共享的 Operator State, 而且每个 key 仅出现在一个分区内。 逻辑上每个 keyed-state 和唯一元组 <算子并发实例, key> 绑定,由于每个 key 仅”属于” 算子的一个并发,因此简化为 <算子, key>。
Keyed State 会按照 Key Group 进行管理。Key Group 是 Flink 分发 Keyed State 的最小单元; Key Group 的数目等于作业的最大并发数。在执行过程中,每个 keyed operator 会对应到一个或多个 Key Group
Operator State 在 Flink 作业的并发改变后,会重新分发状态,分发的策略和 Keyed State 不一样。
对于 Operator State (或者 non-keyed state) 来说,每个 operator state 和一个并发实例进行绑定。 Kafka Connector 是 Flink 中使用 operator state 的一个很好的示例。 每个 Kafka 消费者的并发在 Operator State 中维护一个 topic partition 到 offset 的映射关系。
Keyed State 和 Operator State 分别有两种存在形式:managed and raw.
Managed State 由 Flink 运行时控制的数据结构表示,比如内部的 hash table 或者 RocksDB。 比如 “ValueState”, “ListState” 等。Flink runtime 会对这些状态进行编码并写入 checkpoint。
Raw State 则保存在算子自己的数据结构中。checkpoint 的时候,Flink 并不知晓具体的内容,仅仅写入一串字节序列到 checkpoint。
总结:在flink中无论是Keyed State或者是Operator State都可以两种形式存在Managed State
和Raw State
,推荐使用Managed State
,由于 Flink 可以在修改并发时更好的分发状态数据,并且能够更好的管理内存,因此建议使用 managed state(而不是 raw state)。
savepoint: 手动执行,触发Flink创建还原点
checkpoint:系统定义的checkpoint数据存储到HDFS
Flink是一个有状态的计算框架,所有的计算状态数据存储在内存中或者是RocksDB中,这取决于state backend策略的选取。默认情况下Flink的backend状态数据存在JobManager的内存中,一般用作测试环境(数据集非常小)。如果在生产环境下一般会采取以下两种状态后端:filesysterm,rocksdb.由于流计算在JobManager的管理下会定期的进行checkpoint机制,存储计算的快照信息,快照信息会根据state backend后端实现,将状态数据持久化起来。
fold
使用一个初始值,滚动折叠一个KV DataStream
union
将两个或者多个DataSream组合为一个
connect
连接两个流,允许类型不一致,可以共享状态
split
分流
select
对一个split后的流进行选择操作
iterate
基本概念:在流中创建“反馈(feedback)”循环,通过将一个算子的输出重定向到某个先前的算子。这对于定义不断更新模型的算法特别有用。
迭代的数据流向:DataStream → IterativeStream → DataStream
以下代码以流开始并连续应用迭代体。性别为male的元素将被发送回反馈(feedback)通道,继续迭代,其余元素将向下游转发,离开迭代。
val dataStream = env.socketTextStream("localhost", 8888)
dataStream
.map(line => {
val arr = line.split("\\s")
(arr(0), arr(1), arr(2))
})
.iterate(
iteration => {
var count = 0
iteration.map(t3 => {
count += 1
println(t3 + "\t" + count)
t3
})
(iteration.filter(_._3.equals("male")), iteration.filter(_._3.equals("female")))
})
.print()
结果:
(1,zs,male) 149
(1,zs,male) 150
(1,zs,male) 151
(3,ww,female) 1
16> (3,ww,female)
(1,zs,male) 152
(1,zs,male) 153
(1,zs,male) 154
轮询,会将数据轮询发送给下游任务
随机将数据发送给下游
上游分区的数据会轮询方式发送给下游的子分区,上下游任务并行度呈现整数倍
将上游数据广播给下游所有分区
在Spark中 每一个Task(线程)执行需要消耗一个 Core 线程资源。在Flink中每个SubTask代表着一个线程,每个Task的SubTask会分别运行在不同Task Slot中。但是允许TaskSlot 在不同Task中的SubTask共享。
人工干预Flink的OperatorChain
Flink流算子和Spark区别
任何类型的 keyed state 都可以有 有效期 (TTL)。如果配置了 TTL 且状态值已过期,则会尽最大可能清除对应的值;在使用状态 TTL 前,需要先构建一个StateTtlConfig
配置对象。 然后把配置传递到 state descriptor 中enableTimeToLive启用 TTL 功能:
注意:
NullableSerializer
包装一层。Flink默认情况下并不会主动的删除过期的state,只用使用到该state的时候flink才会对State实行过期检查,将过期数据清除。这可能导致一些不经常使用的数据可能已经过期很长时间,但是因为没有使用的机会导致长时间驻留在Flink的内存中,带来内存浪费。
仅仅是在服务重启的时候,回去加载状态快照信息,在加载的时候检查过期的数据,并且删除数据,但是在程序的运行期间并不会主动的删除过期数据,一般运维人员只能通过定期创建savepoint或者checkpoint然后执行故障恢复才可以释放内存。
注意:如果用户使用的是RocksDB的增量式的检查点机制,该种机制就不起作用。
用户除了可以开启cleanupFullSnapshot在系统快照的时候清除过期的数据,同时还可以开启cleanupInBackground策略,该策略会根据用户使用state backend存储策略自动选择一种后台清理模式。
目前Flink的state backend实现统共分为两大类:heap(堆) state backend和 RocksDB backend,其中基于heap(堆) state backend使用的是
incremental cleanup (增量清理)
而 RocksDB backend使用的是compaction filter(压实过滤器)
清理策略。
Trigger
)触发器(Trigger
)决定了窗口什么时候准备好被窗口函数处理。每个窗口分配器都带有一个默认的 Trigger
。如果默认触发器不能满足你的要求,可以使用 trigger(...)
指定自定义的触发器。
触发器接口有五个方法来对不同的事件做出响应:
- TriggerResult onElement - 只要有元素落入到窗口中,就会回调。
- TriggerResult onProcessingTime - 当用户注册的ProcessingTime定时器时间到达,系统回调。
- TriggerResult onEventTime - 当用户注册的EventTime定时器时间到达,系统回调
- onMerge - 当用户使用SessionWindow的时候,系统在做窗口合并的时候会回调该方法。
- clear() - 当窗口被删除的时候系统会回调
前三个函数决定了如何通过返回一个
TriggerResult
来对其调用事件采取什么操作。TriggerResult
可以是以下之一:
- CONTINUE 什么都不做
- FIRE_AND_PURGE 触发计算,然后清除窗口中的元素
- FIRE 触发计算
- PURGE 清除窗口中的元素
窗口分配器的默认触发器
- GlobalWindow的默认触发器是永不会被触发的 NeverTrigger
- Event-time Window是EventTimeTrigger
- Processing-time Window是ProcessingTimeTrigger
可以使用evictor(…)方法完成操作。驱逐者可以在触发器触发后,应用窗口功能之前或之后从窗口中删除元素
Flink附带了三个预先实施的驱逐程序。这些是:
- CountEvictor:从窗口中保留用户指定数量的元素,并从窗口缓冲区的开头丢弃其余的元素。
- DeltaEvictor:采用DeltaFunction和阈值,计算窗口缓冲区中最后一个元素与其余每个元素之间的增量,并删除增量大于或等于阈值的元素。
- TimeEvictor:以毫秒为单位的间隔作为参数,对于给定的窗口,它将在其元素中找到最大时间戳max_ts,并删除所有时间戳小于max_ts-interval的元素。
重启策略描述的是程序在故障的时候何时进行重启策略。目前Flink给用户提供了以下几种策略:
noRestart - 失败就会终止服务
fixedDelayRestart - 固定重启次数,用户可以设定时间间隔
//每间隔5秒中重启1次,总共尝试5次
fsEnv.setRestartStrategy(RestartStrategies.fixedDelayRestart(5,Time.seconds(5)))
failureRateRestart - 在规定时间间隔内,出错次数达到固定值,认定任务失败
//1分钟内总共失败5次,每次尝试间隔5秒
fsEnv.setRestartStrategy(RestartStrategies.failureRateRestart(5,Time.minutes(1),Time.seconds(5)))
fallBackRestart - 如果集群配置重启策略则使用集群配置策略,如果没有配置默认策略,系统会使用fixedDelayRestart
fsEnv.setRestartStrategy(RestartStrategies.fallBackRestart())
该配置配置系统已何种方式做故障重启,目前Flink支持两种重启策略region
-局部| full
-全部重启,需要用户配置flink-conf。yaml
jobmanager.execution.failover-strategy: region
FlinkCEP是构建在Flink流处理之上的的 Complex Event Processing (CEP)库。允许用户在无止境的数据流中检测到用户所关注的事件模型,并且将关注的事件模型数据抽取出来。
首先,这在Spark中是不允许的,因为Spark会持久化代码片段,一旦修改代码,必须删除Checkpoint,但是Flink仅仅存储各个算子的计算状态,如果用户修改代码,需要用户在有状态的操作算子上指定uid属性。
env.addSource(new FlinkKafkaConsumer[String]("topic01",new SimpleStringSchema(),props))
.uid("kakfa-consumer")
.flatMap(line => line.split("\\s+"))
.map((_,1))
.keyBy(0) //只可以写一个参数
.sum(1)
.uid("word-count") //唯一
.map(t=>t._1+"->"+t._2)
.print()
https://www.jianshu.com/p/c0af87078b9c
比如我们在平时的开发中,需要对数据进行count,sum,max等操作,这些中间的结果(即是状态)是需要保存的,因为要不断的更新,这些值或者变量就可以理解为是一种状态,拿读取kafka为例,我们需要记录数据读取的位置(即是偏移量),并保存offset,这时offest也可以理解为是一种状态.
Flink是怎么保证容错恢复的时候保证数据没有丢失也没有数据的冗余呢?
checkpoint是使Flink 能从故障恢复的一种内部机制。检查点是 Flink 应用状态的一个一致性副本,包括了输入的读取位点。在发生故障时,Flink 通过从检查点加载应用程序状态来恢复,并从恢复的读取位点继续处理,就好像什么事情都没发生一样。Flink的状态存储在Flink的内部,这样做的好处就是不再依赖外部系统,降低了对外部系统的依赖,在Flink的内部,通过自身的进程去访问状态变量.同时会定期的做checkpoint持久化,把checkpoint存储在一个分布式的持久化系统中,如果发生故障,就会从最近的一次checkpoint中将整个流的状态进行恢复.
#### 如何拿到窗口元数据信息
ProcessWindowFunction| WindowFunction (遗产,无法获取窗口 状态信息)
有两种:
将延迟数据加入到一个数据流中进行处理
有两种:
①一种是基于定时Interval(推荐)
②通过记录触发,每来一条记录系统会立即更新水位线。
watermarker是一种机制,告知流计算何时触发窗口(Event Time),
w= max(EventTime)- maxOrderness
watermarker(T) > wondow end (T’)窗口触发。
水位线触发时机:AssignerWithPunctuatedWatermarks 数据触发 、AssignerWithPeriodicWatermarks 定时触发
Flink通过设置.allowedLateness(Time.seconds(2))设置数据最大迟到时间maxlate 。watermarker(T)-maxlate <= wondow end (T’) ,这些数据还可加入窗口计算。默认情况下,如果watermarker(T) -maxlate > window end (T’) ,此时再有数据落入窗口该数据默认会被丢弃。可以通过设置Sideout,完成对迟到数据的处理。
ProcessWindowFunction| WindowFunction (遗产,无法获取窗口 状态信息)