一.Hadoop
1.搭建Hadoop集群的流程?
关闭防火墙
关闭SELINUX
修改主机名
ssh无密码拷贝数据
设置主机名和IP对应
jdk1.8安装
下载并解压Hadoop的jar包
配置hadoop的核心文件
格式化namenode
启动....
2.介绍一下MapReduce的Shuffle过程,并给出Hadoop优化的方案?
mr整体过程
【MapTask】
(1)Read阶段:MapTask通过用户编写的RecordReader,从输入InputSplit中解析出一个个key/value。
(2)Map阶段:该节点主要是将解析出的key/value交给用户编写map()函数处理,并产生一系列新的key/value。
(3)Collect收集阶段:在用户编写map()函数中,当数据处理完成后,一般会调用OutputCollector.collect()输出结果。在该函数内部,它会将生成的key/value分区(调用Partitioner),并写入一个环形内存缓冲区中。
(4)Spill阶段:即“溢写”,当环形缓冲区满后,MapReduce会将数据写到本地磁盘上,生成一个临时文件。需要注意的是,将数据写入本地磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行合并、压缩等操作。
溢写阶段详情:
步骤1:利用快速排序算法对缓存区内的数据进行排序,排序方式是,先按照分区编号Partition进行排序,然后按照key进行排序。这样,经过排序后,数据以分区为单位聚集在一起,且同一分区内所有数据按照key有序。
步骤2:按照分区编号由小到大依次将每个分区中的数据写入任务工作目录下的临时文件output/spillN.out(N表示当前溢写次数)中。如果用户设置了Combiner,则写入文件之前,对每个分区中的数据进行一次聚集操作。
步骤3:将分区数据的元信息写到内存索引数据结构SpillRecord中,其中每个分区的元信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小。如果当前内存索引大小超过1MB,则将内存索引写到文件output/spillN.out.index中。
(5)Combine阶段:当所有数据处理完成后,MapTask对所有临时文件进行一次合并,以确保最终只会生成一个数据文件。
【ReduceTask】
(1)Copy阶段:ReduceTask从各个MapTask上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。
(2)Merge阶段:在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。
(3)Sort阶段:按照MapReduce语义,用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起,Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此,ReduceTask只需对所有数据进行一次归并排序即可。
(4)Reduce阶段:reduce()函数将计算结果写到HDFS上。
shuffle 阶段
1)Map方法之后,Reduce方法之前的数据处理过程称之为Shuffle
2)Map方法之后,数据首先进入到分区方法,把数据标记好分区,然后把数据发送到环形缓冲区;环形缓冲区默认大小100m,环形缓冲区达到80%时,进行溢写;溢写前对数据进行排序,排序按照对key的索引进行字典顺序排序,排序的手段「快排」;溢写产生大量溢写文件,需要对溢写文件进行「归并排序」;对溢写的文件也可以进行Combiner操作,前提是汇总操作,求平均值不行。最后将文件按照分区存储到磁盘,等待Reduce端拉取。
3)每个Reduce拉取Map端对应分区的数据。拉取数据后先存储到内存中,内存不够了,再存储到磁盘。拉取完所有数据后,采用归并排序将内存和磁盘中的数据都进行排序。在进入Reduce方法前,可以对数据进行分组操作。
优化
从压缩,小文件,集群优化层面
【小文件优化】
1)HDFS小文件影响
影响NameNode的寿命,因为文件元数据存储在NameNode的内存中
影响计算引擎的任务数量,比如每个小的文件都会生成一个Map任务
2)数据输入小文件处理
合并小文件:对小文件进行归档(Har)、自定义Inputformat将小文件存储成SequenceFile文件。
采用ConbinFileInputFormat来作为输入,解决输入端大量小文件场景
对于大量小文件Job,可以开启JVM重用
【mr任务优化】
1)Map阶段
(1)增大环形缓冲区大小。由100m扩大到200m
(2)增大环形缓冲区溢写的比例。由80%扩大到90%
(3)减少对溢写文件的merge次数。(10个文件,一次20个merge)
(4)不影响实际业务的前提下,采用Combiner提前合并,减少 I/O。
2)Reduce阶段
(1)合理设置Map和Reduce数:两个都不能设置太少,也不能设置太多。太少,会导致Task等待,延长处理时间;太多,会导致 Map、Reduce任务间竞争资源,造成处理超时等错误。
(2)设置Map、Reduce共存:调整slowstart.completedmaps参数,使Map运行到一定程度后,Reduce也开始运行,减少Reduce的等待时间。
(3)规避使用Reduce,因为Reduce在用于连接数据集的时候将会产生大量的网络消耗。
(4)增加每个Reduce去Map中拿数据的并行数
(5)集群性能可以的前提下,增大Reduce端存储数据内存的大小。
3)IO传输
采用数据压缩的方式,减少网络IO的的时间。安装Snappy和LZOP压缩编码器。
压缩:
(1)map输入端主要考虑数据量大小和切片,支持切片的有Bzip2、LZO。注意:LZO要想支持切片必须创建索引;
(2)map输出端主要考虑速度,速度快的snappy、LZO;
(3)reduce输出端主要看具体需求,例如作为下一个mr输入需要考虑切片,永久保存考虑压缩率比较大的gzip。
4)整体
(1)NodeManager默认内存8G,需要根据服务器实际配置灵活调整,例如128G内存,配置为100G内存左右,yarn.nodemanager.resource.memory-mb。
(2)单容器默认内存8G,需要根据该任务的数据量灵活调整,例如128m数据,配置1G内存,yarn.scheduler.maximum-allocation-mb。
(3)mapreduce.map.memory.mb :控制分配给MapTask内存上限,如果超过会kill掉进程(报:Container is running beyond physical memory limits. Current usage:565MB of512MB physical memory used;Killing Container)。默认内存大小为1G,如果数据量是128m,正常不需要调整内存;如果数据量大于128m,可以增加MapTask内存,最大可以增加到4-5g。
(4)mapreduce.reduce.memory.mb:控制分配给ReduceTask内存上限。默认内存大小为1G,如果数据量是128m,正常不需要调整内存;如果数据量大于128m,可以增加ReduceTask内存大小为4-5g。
(5)mapreduce.map.java.opts:控制MapTask堆内存大小。(如果内存不够,报:java.lang.OutOfMemoryError)
(6)mapreduce.reduce.java.opts:控制ReduceTask堆内存大小。(如果内存不够,报:java.lang.OutOfMemoryError)
(7)可以增加MapTask的CPU核数,增加ReduceTask的CPU核数
(8)增加每个Container的CPU核数和内存大小
(9)在hdfs-site.xml文件中配置多目录(多磁盘)
7) 压缩
压缩,可以参考这张图
gzip压缩
「应用场景」:当每个文件压缩之后在130M以内的(1个块大小内),都可以考虑用gzip压缩格式。譬如说一天或者一个小时的日志压缩成一个gzip文件,运行mapreduce程序的时候通过多个gzip文件达到并发。hive程序,streaming程序,和java写的mapreduce程序完全和文本处理一样,压缩之后原来的程序不需要做任何修改。
「优点」:压缩率比较高,而且压缩/解压速度也比较快;hadoop本身支持,在应用中处理gzip格式的文件就和直接处理文本一样;有hadoop native库;大部分linux系统都自带gzip命令,使用方便。
「缺点」:不支持split。
snappy压缩
「应用场景」:当mapreduce作业的map输出的数据比较大的时候,作为map到reduce的中间数据的压缩格式;或者作为一个mapreduce作业的输出和另外一个mapreduce作业的输入。
「优点」:高速压缩速度和合理的压缩率;支持hadoop native库。
「缺点」:不支持split;压缩率比gzip要低;hadoop本身不支持,需要安装;linux系统下没有对应的命令。
lzo压缩
「应用场景」:一个很大的文本文件,压缩之后还大于200M以上的可以考虑,而且单个文件越大,lzo优点越越明显。
「优点」:压缩/解压速度也比较快,合理的压缩率;支持split,是hadoop中最流行的压缩格式;支持hadoop native库;可以在linux系统下安装lzop命令,使用方便。
「缺点」:压缩率比gzip要低一些;hadoop本身不支持,需要安装;在应用中对lzo格式的文件需要做一些特殊处理(为了支持split需要建索引,还需要指定inputformat为lzo格式)。
bzip2压缩
「应用场景」:适合对速度要求不高,但需要较高的压缩率的时候,可以作为mapreduce作业的输出格式;或者输出之后的数据比较大,处理之后的数据需要压缩存档减少磁盘空间并且以后数据用得比较少的情况;或者对单个很大的文本文件想压缩减少存储空间,同时又需要支持split,而且兼容之前的应用程序(即应用程序不需要修改)的情况。
「优点」:支持split;具有很高的压缩率,比gzip压缩率都高;hadoop本身支持,但不支持native;在linux系统下自带bzip2命令,使用方便。
「缺点」:压缩/解压速度慢
3.Hadoop的参数优化?
1.在hdfs-site.xml文件中配置多目录,最好提前配置好,否则更改目录需要重新启动集群
2.NameNode有一个工作线程池,用来处理不同DataNode的并发心跳以及客户端并发的元数据操作
dfs.namenode.handler.count=20 * log2(Cluster Size)
比如集群规模为10台时,此参数设置为60
3.编辑日志存储路径dfs.namenode.edits.dir设置与镜像文件存储路径dfs.namenode.name.dir尽量分开,达到最低写入延迟
4.服务器节点上YARN可使用的物理内存总量,默认是8192(MB),注意,如果你的节点内存资源不够8GB,则需要调减小这个值,而YARN不会智能的探测节点的物理内存总量
5.单个任务可申请的最多物理内存量,默认是8192(MB)
[资源相关参数]
配置参数 参数说明
mapreduce.map.memory.mb 一个MapTask可使用的资源上限(单位:MB),默认为1024。如果MapTask实际使用的资源量超过该值,则会被强制杀死。
mapreduce.reduce.memory.mb 一个ReduceTask可使用的资源上限(单位:MB),默认为1024。如果ReduceTask实际使用的资源量超过该值,则会被强制杀死。
mapreduce.map.cpu.vcores 每个MapTask可使用的最多cpu core数目,默认值: 1
mapreduce.reduce.shuffle.parallelcopies 每个ReduceTask可使用的最多cpu core数目,默认值: 1
mapreduce.reduce.shuffle.merge.percent 每个Reduce去Map中取数据的并行数。默认值是5
mapreduce.reduce.shuffle.input.buffer.percent Buffer中的数据达到多少比例开始写入磁盘。默认值0.66
mapreduce.reduce.input.buffer.percent Buffer大小占Reduce可用内存的比例。默认值0.7 指定多少比例的内存用来存放Buffer中的数据,默认值是0.0
[YARN]
配置参数 参数说明
yarn.scheduler.minimum-allocation-mb 给应用程序Container分配的最小内存,默认值:1024
yarn.scheduler.maximum-allocation-mb 给应用程序Container分配的最大内存,默认值:8192
yarn.scheduler.minimum-allocation-vcores 每个Container申请的最小CPU核数,默认值:1
yarn.scheduler.maximum-allocation-vcores 每个Container申请的最大CPU核数,默认值:32
yarn.nodemanager.resource.memory-mb 给Containers分配的最大物理内存,默认值:8192
[Shuffle]
配置参数 参数说明
mapreduce.task.io.sort.mb Shuffle的环形缓冲区大小,默认100m
mapreduce.map.sort.spill.percent 环形缓冲区溢出的阈值,默认80%
[容错相关参数]
配置参数 参数说明
mapreduce.map.maxattempts 每个Map Task最大重试次数,一旦重试参数超过该值,则认为Map Task运行失败,默认值:4。
mapreduce.reduce.maxattempts 每个Reduce Task最大重试次数,一旦重试参数超过该值,则认为Map Task运行失败,默认值:4。
mapreduce.task.timeout Task超时时间,经常需要设置的一个参数,该参数表达的意思为:如果一个Task在一定时间内没有任何进入,即不会读取新的数据,也没有输出数据,则认为该Task处于Block状态,可能是卡住了,也许永远会卡住,为了防止因为用户程序永远Block住不退出,则强制设置了一个该超时时间(单位毫秒),默认是600000。如果你的程序对每条输入数据的处理时间过长(比如会访问数据库,通过网络拉取数据等),建议将该参数调大。
4.hadoop基准测试?
搭建完Hadoop集群后需要对HDFS读写性能和MR计算能力测试。测试jar包在hadoop的share文件夹下。
集群总吞吐量 = 带宽*集群节点个数/副本数
例如:100m/s * 10台/ 3= 333m/s
注意:如果测试数据在本地,那副本数-1。因为这个副本不占集群吞吐量。如果数据在集群外,向该集群上传,需要占用带宽。本公式就不用减1。
5.Hadoop宕机?
1.如果MR造成系统宕机。此时要控制Yarn同时运行的任务数,和每个任务申请的最大内存。调整参数:yarn.scheduler.maximum-allocation-mb(单个任务可申请的最多物理内存量,默认是8192MB)。
2.如果写入文件过量造成NameNode宕机。那么调高Kafka的存储大小,控制从Kafka到HDFS的写入速度。高峰期的时候用Kafka进行缓存,高峰期过去数据同步会自动跟上。
6.hadoop数据倾斜?
1)提前在map进行combine,减少传输的数据量
在Mapper加上combiner相当于提前进行reduce,即把一个Mapper中的相同key进行了聚合,减少shuffle过程中传输的数据量,以及Reducer端的计算量。
如果导致数据倾斜的key大量分布在不同的mapper的时候,这种方法就不是很有效了。
2)导致数据倾斜的key 大量分布在不同的mapper
(1)局部聚合加全局聚合。
第一次在map阶段对那些导致了数据倾斜的key 加上1到n的随机前缀,这样本来相同的key 也会被分到多个Reducer中进行局部聚合,数量就会大大降低。
第二次mapreduce,去掉key的随机前缀,进行全局聚合。
思想:二次mr,第一次将key随机散列到不同reducer进行处理达到负载均衡目的。第二次再根据去掉key的随机前缀,按原key进行reduce处理。
这个方法进行两次mapreduce,性能稍差。
(2)增加Reducer,提升并行度
JobConf.setNumReduceTasks(int)
(3)实现自定义分区
根据数据分布情况,自定义散列函数,将key均匀分配到不同Reducer
7.Hadoop 异构存储(冷热数据分离)
所谓的异构存储就是将不同需求或者冷热的数据存储到不同的介质中去,实现既能兼顾性能又能兼顾成本。
[存储类型]
HDFS异构存储支持如下4种类型,分别是:
RAM_DISK(内存镜像文件系统)
SSD(固态硬盘)
DISK(普通磁盘,HDFS默认)
ARCHIVE(计算能力弱而存储密度高的存储介质,一般用于归档)
以上四种自上到下,速度由快到慢,单位存储成本由高到低。
[存储策略]
HDFS总共支持Lazy_Persist、All_SSD、One_SSD、Hot、Warm和Cold等6种存储策略。
策略 说明
Lazy_Persist 1份数据存储在[RAM_DISK]即内存中,其他副本存储在DISK中
All_SSD 全部数据都存储在SSD中
One_SSD 一份数据存储在SSD中,其他副本存储在DISK中
Hot 全部数据存储在DISK中,默认策略为Hot
Warm 一份数据存储在DISK中,其他数据存储方式为ARCHIVE
Cold 全部数据以ARCHIVE的方式保存
hdfs
1.hdfs的读写流程?
读
(1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
(2)NameNode返回是否可以上传。
(3)客户端请求第一个 Block上传到哪几个DataNode服务器上。
(4)NameNode返回3个DataNode节点,分别为dn1、dn2、dn3。
(5)客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
(6)dn1、dn2、dn3逐级应答客户端。
(7)客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。
(8)当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
写
(1)客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
(2)挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
(3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
(4)客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。
2.NameNode和Secondary NameNode工作机制?
【NameNode的结构】
Fsimage
Fsimage文件是HDFS文件系统元数据的一个永久性检查点,其中包含HDFS文件系统的所有目录和文件inode的序列化信息。
Edits文件
存放HDFS文件系统的所有更新操作的逻辑,文件系统客户端执行的所有写操作首先会记录大Edits文件中。
Seen_txid
文件保存是一个数字,就是最后一个edits_的数字。
【NameNode的工作机制】
第一阶段:NameNode启动
(1)第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
(2)客户端对元数据进行增删改的请求。
(3)NameNode记录操作日志,更新滚动日志。
(4)NameNode在内存中对元数据进行增删改。
第二阶段:Secondary NameNode工作
(1)Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否检查结果。
(2)Secondary NameNode请求执行CheckPoint。
(3)NameNode滚动正在写的Edits日志。
(4)将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode。
(5)Secondary NameNode加载编辑日志和镜像文件到内存,并合并。
(6)生成新的镜像文件fsimage.chkpoint。
(7)拷贝fsimage.chkpoint到NameNode。
(8)NameNode将fsimage.chkpoint重新命名成fsimage。
3.DataNode工作机制?
(1)一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
(2)DataNode启动后向NameNode注册,通过后,周期性(1小时)的向NameNode上报所有的块信息。
(3)心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。
(4)集群运行中可以安全加入和退出一些机器。
4.hdfs的小文件处理?
1)会有什么影响
(1)存储层面:
HDFS上每个文件都要在NameNode上建立一个索引,这个索引的大小约为150byte,这样当小文件比较多的时候,就会 产生很多的索引文件,一方面会大量占用NameNode的内存空间.
(2)计算层面:
每个小文件都会起到一个MapTask,占用了大量计算资源
2)怎么解决
(1)采用har归档方式,将小文件归档
(2)采用CombineTextInputFormat
(3)有小文件场景开启JVM重用;如果没有小文件,不要开启JVM重用,因为会一直占用使用到的task卡槽,直到任务完成才释放。
JVM重用可以使得JVM实例在同一个job中重新使用N次,N的值可以在Hadoop的mapred-site.xml文件中进行配置。通常在10-20之间
5.NameNode内存?
1)Hadoop2.x系列,配置NameNode默认2000m
2)Hadoop3.x系列,配置NameNode内存是动态分配的
NameNode内存最小值1G,每增加100万个block,增加1G内存。
yarn
1.job 提交流程?
简略版对应的步骤:
1.client向RM提交应用程序,其中包括启动该应用的ApplicationMaster的必须信息,例如ApplicationMaster程序、启动ApplicationMaster的命令、用户程序等
2.ResourceManager启动一个container用于运行ApplicationMaster
3.启动中的ApplicationMaster向ResourceManager注册自己,启动成功后与RM保持心跳
4.ApplicationMaster向ResourceManager发送请求,申请相应数目的container
5.申请成功的container,由ApplicationMaster进行初始化。container的启动信息初始化后,AM与对应的NodeManager通信,要求NM启动container
6.NM启动container
7.container运行期间,ApplicationMaster对container进行监控。container通过RPC协议向对应的AM汇报自己的进度和状态等信息
8.应用运行结束后,ApplicationMaster向ResourceManager注销自己,并允许属于它的container被收回
2.yarn调度器?
Hadoop调度器主要分为三类:
1.FIFO Scheduler:先进先出调度器:优先提交的,优先执行,后面提交的等待【生产环境不会使用】
2.Capacity Scheduler:容量调度器:允许看创建多个任务对列,多个任务对列可以同时执行。但是一个队列内部还是先进先出。【Hadoop2.7.2默认的调度器】
3.Fair Scheduler:公平调度器:第一个程序在启动时可以占用其他队列的资源(100%占用),当其他队列有任务提交时,占用资源的队列需要将资源还给该任务。还资源的时候,效率比较慢。【CDH版本的yarn调度器默认】
二.Hive
1.hive的架构?
解、编、优、执
2.hive的join和group by的底层实现原理?
3.Hive数据倾斜(⭐️)
--现象
1.某个reduce task,卡在99.9%半天不动。
2.任务超时被杀掉
--问题排查
1)如何判断大key导致的问题?
1.通过时间判断
观察reduce任务,发现某个reduce任务执行时长远大于其他任务。
2.通过任务的counter判断。
在WebUI找到执行的mr任务,找到reduce任务,然后查看每个reduce task的counter,查看reduce通过输入记录数,
会发现出现倾斜的reducetask的输入记录数远大于其他reduce任务。
--定位SQL代码?
确定任务卡住的stage
1.一般通过Hive的默认jobname会带上名称会带上stage阶段。
2.如果自定义的jobname,可以找到执行时长最长的task在日志中搜索'join struct',找到join的key,根据这个信息参考该SQL的执行计划。通过参考执行计划,可以找到stage阶段。
可以看出那段sql代码出现了倾斜。
--解决
1) 单表 -- group by id
(1) 按照id分组计算count值
-> 单个Key
-> 多个Key
(2) 单个Key
两阶段聚合的方式,(局部聚合+全局聚合),加随机数局部聚合后,去掉随机数。不影响业务逻辑。
配置参数,双重聚合 set hive.groupby.skewindata = true;
过滤出这个Key单独处理(=key union all !=key)
(3) 多个Key
增加Reducer个数,一定程度上解决问题
自定义分区器
两阶段聚合的方式,(局部聚合+全局聚合),加随机数局部聚合后,去掉随机数。
配置参数,双重聚合 set hive.groupby.skewindata = true;
2) JOIN ON 关联字段
(1) 大表JOIN小表 mapJoin 避免了Reducer
(2) 大表JOIN大表 A表加随机数 B表扩容 聚合
A concat(id,'_',随机数[1,2,3])
B
concat(id,'_',1)
union all
concat(id,'_',2)
union all
concat(id,'_',3)
4.Hive 小文件过多怎么解决?
--影响
产生小文件的产生有三个地方,map输入,map输出,reduce输出,小文件过多也会影响hive的分析效率
存储层面:1个block,占用namenode150字节内存,128G内存也只能存储 9亿文件块
计算层面:每个小文件都会起到一个MapTask,占用了大量计算资源
--怎么解决?
(1)采用har归档方式,将小文件归档
(2)采用CombineTextInputFormat
(3)有小文件场景开启JVM重用;如果没有小文件,不要开启JVM重用,因为会一直占用使用到的task卡槽,直到任务完成才释放。JVM重用可以使得JVM实例在同一个job中重新使用N次,N的值可以在Hadoop的mapred-site.xml文件中进行配置(mapreduce.job.jvm.numtasks)。通常在10-20之间
(4)相关参数调优:
--设置map输入的小文件合并
set mapred.max.split.size=256000000;
//一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
set mapred.min.split.size.per.node=100000000;
//一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)
set mapred.min.split.size.per.rack=100000000;
//执行Map前进行小文件合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
--设置map输出和reduce输出进行合并的相关参数:
//设置map端输出进行合并,默认为true
set hive.merge.mapfiles = true
//设置reduce端输出进行合并,默认为false
set hive.merge.mapredfiles = true
//设置合并文件的大小
set hive.merge.size.per.task = 256*1000*1000
//当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge。
set hive.merge.smallfiles.avgsize=16000000
5.Hive优化
--(1)建表优化
1.建分区表
2.采取合适的文件格式和压缩。
--(2)HQL优化
--单表优化
列裁剪、分区裁剪
--多表优化
谓词下推、
map join、
SMB join、
--(3)Job优化
--合理设置map数
复杂文件增加map数、
小文件合并、
map端聚合:map端执行combiner
--合理设置reduce数
调整reduce个数:单个reduce处理数据量(256M)和单个任务reduce个数(1009)决定
reducer数量与输出文件的数量相关。如果reducer数太多,会产生大量小文件,对HDFS造成压力。
如果reducer数太少,每个reducer要处理很多数据,容易拖慢运行时间或者造成OOM。
--整体任务优化
Fetch抓取:Hive中对某些情况的查询可以不必使用MapReduce计算,如:select *
本地模式:对于小数据集,单机处理所有任务。
并行执行:一个job多个阶段,如果不相互依赖,有的阶段可以并行执行。
严格模式:(1)分区表,不允许全表扫描。(2)order by必须加limit (3)限制笛卡尔积。
JVM重用:避免小文件
推测执行:为长时间任务开启一个备用任务,选运行时长小的作为输出结果。
6.ORC、Parquet等列式存储的优点?
ORC和Parquet都是高性能的存储方式,这两种存储格式总会带来存储和性能上的提升。
Parquet:
Parquet支持嵌套的数据模型,类似于Protocol Buffers,每一个数据模型的schema包含多个字段,每一个字段有三个属性:重复次数、数据类型和字段名。
重复次数可以是以下三种:required(只出现1次),repeated(出现0次或多次),optional(出现0次或1次)。每一个字段的数据类型可以分成两种:group(复杂类型)和primitive(基本类型)。
Parquet中没有Map、Array这样的复杂数据结构,但是可以通过repeated和group组合来实现的。
由于Parquet支持的数据模型比较松散,可能一条记录中存在比较深的嵌套关系,如果为每一条记录都维护一个类似的树状结可能会占用较大的存储空间,因此Dremel论文中提出了一种高效的对于嵌套数据格式的压缩算法:Striping/Assembly算法。通过Striping/Assembly算法,parquet可以使用较少的存储空间表示复杂的嵌套格式,并且通常Repetition level和Definition level都是较小的整数值,可以通过RLE算法对其进行压缩,进一步降低存储空间。
Parquet文件是以二进制方式存储的,是不可以直接读取和修改的,Parquet文件是自解析的,文件中包括该文件的数据和元数据。
ORC:
ORC文件是自描述的,它的元数据使用Protocol Buffers序列化,并且文件中的数据尽可能的压缩以降低存储空间的消耗。
和Parquet类似,ORC文件也是以二进制方式存储的,所以是不可以直接读取,ORC文件也是自解析的,它包含许多的元数据,这些元数据都是同构ProtoBuffer进行序列化的。
ORC会尽可能合并多个离散的区间尽可能的减少I/O次数。
ORC中使用了更加精确的索引信息,使得在读取数据时可以指定从任意一行开始读取,更细粒度的统计信息使得读取ORC文件跳过整个row group,ORC默认会对任何一块数据和索引信息使用ZLIB压缩,因此ORC文件占用的存储空间也更小。
在新版本的ORC中也加入了对Bloom Filter的支持,它可以进一 步提升谓词下推的效率,在Hive 1.2.0版本以后也加入了对此的支 持。
- ORC:ORC文件是自描述的,它的元数据使用Protocol Buffers序列化,文件中的数据尽可能的压缩以降低存储空间的消耗;以二进制方式存储,不可以直接读取;自解析,包含许多元数据,这些元数据都是同构ProtoBuffer进行序列化的;会尽可能合并多个离散的区间尽可能的减少I/O次数;在新版本的ORC中也加入了对Bloom Filter的支持,它可以进一 步提升谓词下推的效率,在Hive 1.2.0版本以后也加入了对此的支 持。
- Parquet:Parquet支持嵌套的数据模型,类似于Protocol Buffers,每一个数据模型的schema包含多个字段,每一个字段有三个属性:重复次数、数据类型和字段名;Parquet中没有Map、Array这样的复杂数据结构,但是可以通过repeated和group组合来实现;通过Striping/Assembly算法,parquet可以使用较少的存储空间表示复杂的嵌套格式,并且通常Repetition level和Definition level都是较小的整数值,可以通过RLE算法对其进行压缩,进一步降低存储空间;Parquet文件以二进制方式存储,不可以直接读取和修改,Parquet文件是自解析的,文件中包括该文件的数据和元数据。
7.Hive的map join(小表 join 大表)、smb join(大表 join 大表)、left
semi join
--map join(小表 join 大表)
将大表分成多个切片,小表广播到各个切片中,在map阶段完成join,没有reduce阶段。
MapJoin 是将 Join 双方比较小的表直接分发到各个Map进程的内存中,在 Map 进程中进行 Join 操作,这样就不用进行 Reduce 步骤,从而提高了速度。
参数设置:
(1)设置自动选择MapJoin
set hive.auto.convert.join=true; #默认为true
(2)小表的阈值设置(默认25M以下认为是小表):
set hive.mapjoin.smalltable.filesize=25000000;
注意:执行 left join,大小表join不管谁做主表都会失效,如:小表 left join 大表,大表被分成多个切片,小表没join上的数据不知道是否该保留。
--smb join(大表 join 大表)
SMB Join :Sort Merge Bucket Join
2张表都建成分桶表
clustered by(id)
sorted by(id)
into 6 buckets
将2个大表分成多个桶,基于同一个字段做hash,能join上的数据都在同一个桶中,拿到对应分桶号的数据join之后,在把数据union一起,分而治之。
--left semi join
a left semi join b on a.id = b.id:取出a在b中存在的id的数据。(a.id in b.id)
8.map数、reduce数、job数怎么确定?
map task:根据输入文件的个数,单个文件大于128M会进行切分
reduce task:默认,单个reduce处理256M和单个任务最大的reduce的个数共同决定,输出的reduce个数
job数:
9.cube、rollup、grouping sets 系统函数
常用于OLAP中,不能累加,而且需要根据不同维度上钻和下钻的指标统计
With cube:group by 后面的各种维度都组合统计出来。
with rollup:层级维度的组合处理,假如现在的组合是省+市+区三个维度进行组合,with rollup 就会自动的一级一级往上卷,变成省+市,最后是省
Grouping sets:用户自定义维度组合
10.系统函数
--排序
row_number(),rank(),dense_rank()
row_number: 按顺序编号,不留空位
rank: 按顺序编号,相同的值编相同号,留空位
dense_rank: 按顺序编号,相同的值编相同的号,不留空位
lag(col,n,default_val):往前第 n 行数据
lead(col,n, default_val):往后第 n 行数据
first_value(url):分组窗口的第一个值
last_value(url):分组窗口的最后一个值
ntile(n):将数据分为n组。
--行转列、列转行
lateral view
explode(split(names,',')) temp_tab as `name`
collect_set
group by
11.只有一个reduce?
a)没有group by的汇总,比如把select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt; 写成 select count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04'; 这点非常常见,希望大家尽量改写。
b)用了Order by
c)有笛卡尔积
三.Kafka
1.Kafka的集群架构?
producer、broker、consumer、topic、partition、leader、follower
2.生产者分区策略?
1.指定partition,直接发送指定的partition
2.未指定partition但是有key,会将key的hash值和分区数取余得到分区号。
3.未知的partition且没有key,第一次调用生成随机(后面每次调用+1),和分区数取余得到分区号,round-robin。
3.kafka可靠性保证?
为了保证producer发送的数据能可靠的发往到指定topic,kafka有个ack应答机制,当producer收到ack应答才进行下一轮发送。
--ACK机制
0:leader接受到消息未写入磁盘,返回ack。可能丢数据
1:leader接受到消息并且写入磁盘,返回ack。弱还未同步到follwer,leader挂了,丢数据。
-1:leader接受到消息&&写入磁盘&&follower同步完成,返回ack。同步完成,未返回ack,leader挂了,数据重复。
--副本同步策略
1.半数以上同步完成
2.全部同步完成
--ISR
Leader动态维护一个保持同步的的follower集合,如果follower长时间未向leader同步数据,会被提出ISR队列。
提出队列:1.早期延迟条数 2.同步时间
LEO:每个副本最大的offset
HW:所有副本(ISR)最小的LEO
--故障恢复
follwer挂掉:会被临时踢出ISR队列,待follwer恢复后,会读取挂掉的HW,然后截取超过HW的部分,再从leader同步,当LEO追上当前分区的HW就会被重新加入ISR
leader挂掉:leader挂掉会从ISR中选出一个新的leader,各个follower将高于HW的部分截取掉,从新leader开始同步。
注:只能副本之间的数据一致性,不能保证不重不丢
4.Kafka消费分区分配策略?
--1.Range
对同步一topic的分区进行序号排序,消费者按照字母排序,用分区数%消费者线程数,除不尽,前面的消费者多消费一个分区
如:10个分区,3个消费者,则消费者1:0,1,2,3;消费者2:4,5,6;消费者3;7,8,9
--2.RoundRobin
分区和消防者字典排序,然后将分区轮询分给消费者。
--3.Sticky粘性分区
从0.11版本开始引入,
5.Kafka的offset怎么维护?
0.9版本前在zk中
0.9后,保证在kafka内置topic,__consumer_offsets。
6.kafka为什么这么快?
1.分区技术,并发高
2.顺序写磁盘
消息被顺序追加到log文件的末尾,官网说顺序写600M/s,随机写100K/s
3.零拷贝技术
传统copy:文件->页缓存->应用程序缓存->Socket缓存->网卡
零拷贝:直接读取文件发出去。
7.Kafka消费能力不足怎么处理?
1.Kafka消费能力不足:增加分区数,同时提升消费者线程数。
2.下游处理能力不足:提高下游每批次拉去数据的梳理。
8.kafka事务?
为了实现跨分区跨会话的事务,全局的事务ID+PID保证重启也不会重复。
9.Kafka参数优化?
1.日志保留策略
2.副本数
3.网络通信延时
如果网络不好,会到时副本丢失ISR,会出现频繁复制副本
4.Kafka内存调整
默认1个G,生产不超过6个G。
四.Hbase
1.hbase的好处?
横向扩展
列式存储
高并发、随机读写
自动故障恢复、负载均衡
2.HBase架构与角色?
namespace
table
region:将高表横向切分成一个分片,有起始rowkey,
rowkey
column family
column
timestamp:标识不同版本的数据
cell:rowkey+timestamp+列族+列
3.HBase存储结构?
4.HBase读写流程?
写流程
1.Client请求ZK,获取meta表所在的region server。
2.去指定的region server找meta表,根据meta表获取该数据写到哪个region sever的哪个Region。
同时将meta表缓存在客户端 meta cache,便于下次访问。
3.访问指定的Region Server中的Region,将数据写入HLog和MemStroe。
4.向Client返回ACK(写入内存)
5.MemStore到达一定阈值会刷写到StoreFile中。
6.当多个StoreFile到达一定大小会触发Compact操作,合并到一个StoreFile。
7.当合并的StoreFile到达一定大小会Split,Region分割。
#刷写
MemStore级别
Region级别
RegionServer级别
HLog级别
定时
手动
#合并
Minor:将小文件合并成大文件,不会清理过期和删除的数据。
Major:将小文件合并成大文件,会清理过期和删除的数据。
#Region Slit
0.94版本之前:单个StoreFile达到一定大小
0.94版本-2.0版本:Region下所有StoreFile一定大小
读流程
1.Client读取ZK获取meta表所在的RegionServer。
2.访问RegionServer,获取meta表,根据meta表获取读取数据坐在的RegionServer的Region,同时将meta表缓存在Client,以便下次访问。
3.访问RegionServer的指定Region,读取BlockCache>MemStore>HFile查找数据,将HFile读到的数据缓存在BlockCache(读缓存),将查到的所有结果(1个rowkey不同版本和类型)数据合并返回给Client。
5.预分区?
手动设定预分区:create
16进制生产预分区
6.Rowkey设计?
目的:数据均匀的分布于所有的region中,在一定程度上防止数据倾斜
1.唯一性:主键
2.长度:60-80字节,最大64k
3.散列(随机数、hash、反转、加盐):rowkey没有规律,均匀分配。
具体案例:
1.确定分区号(散列)
XX = md5(学生id)
2.确定rowkey(唯一、长度)
XX_学生id_购课id
7.Hbase优化?
8.二级索引?
建立二级索引提升查询效率
五.Spark
六.Flink
1.Flink基础
1)介绍下flink?
Lambda架构
Kappa架构
2)Flink和Spark的区别?
这个问题是一个非常宏观的问题,因为两个框架的不同点非常之多。但是在面试时有
--非常重要的一点一定要回答出来:
Flink 是标准的实时处理引擎,基于事件驱动。而 Spark Streaming 是微批(Micro-Batch)的模型。
下面我们就分几个方面介绍两个框架的主要区别:
1)架构模型 Spark Streaming 在运行时的主要角色包括:Master、Worker、Driver、Executor, Flink 在运行时主要包含:Jobmanager、Taskmanager 和 Slot。
2)任务调度 Spark Streaming 连续不断的生成微小的数据批次,构建有向无环图 DAG, Spark Streaming 会依次创建 DStreamGraph、JobGenerator、JobScheduler。Flink 根据用户 提交的代码生成 StreamGraph,经过优化生成 JobGraph,然后提交给 JobManager 进行处理, JobManager 会根据 JobGraph 生成 ExecutionGraph,ExecutionGraph 是 Flink 调度最核心 的数据结构,JobManager 根据 ExecutionGraph 对 Job 进行调度。
3)时间机制 Spark Streaming 支持的时间机制有限,只支持处理时间。 Flink 支持了 流处理程序在时间上的三个定义:处理时间、事件时间、注入时间。同时也支持 watermark 机制来处理滞后数据。
4)容错机制对于 Spark Streaming 任务,我们可以设置 checkpoint,然后假如发生故 障并重启,我们可以从上次 checkpoint 之处恢复,但是这个行为只能使得数据不丢失,可 能会重复处理,不能做到恰好一次处理语义。Flink 则使用两阶段提交协议来解决这个问题。
2.Flink核心原理
分区策略
并行度
常用算子
检查点(checkpoint)
状态
分类
存储
存储 | 描述 | 本地状态 | 状态备份 |
---|---|---|---|
MemoryStateBackend | 纯内存,适用于验证、测试,不推荐生产环境 | TaskManager | JobManager |
FsStateBackend | 内存+文件,适用于长周期大规模的数据 | TaskManager | HDFS |
RocksDBStateBackend | RocksDB,适用于长周期大规模的数据 | RocksDB | HDfS |
状态数据的存储和访问
状态数据的备份和恢复
状态数据的划分和动态扩容
状态数据的清理
时间
分类
1.事件时间:数据实际产生的时间
2.处理时间:flink算子处理的时间
3.注入时间:进入flink程序的时间
watermark
--作用
用于处理乱序事件,而正确地处理乱序事件,通常用Watermark机制结合窗口来实现
--生成方式
AssingerWithPeriodicWatermarks (周期性的生成Watermark策略,不会针对每个事件都生成)
AssingerWithPunctuatedWatermarks (对每个事件都尝试进行Watermark的生成,如果生成的结果是null 或Watermark小于之前的,则不会发往下游)
乱序迟到问题处理
窗口
分类
Flink支持两种划分窗口的方式(time和count),第一种,按时间驱动进行划分、另一种按数据驱动进行划分。
--time
1.滑动窗口 2.滚动窗口 3.
--count
1.滑动 2.滚动
--会话
实现原理
WindowAssigner(用来决定某个元素被分配到哪个/哪些窗口中去)
WindowTrigger(决定一个窗口何时能够呗计算或清除,每一个窗口都拥有一个属于自己的Trigger)
WindowEvictor(窗口数据的过滤器,可在Window Function 执行前或后,从Window中过滤元素)
CountEvictor:计数过滤器。在Window中保留指定数量的元素,并从窗口头部开始丢弃其余元素
DeltaEvictor:阈值过滤器。丢弃超过阈值的数据记录
TimeEvictor:时间过滤器。保留最新一段时间内的元素
3.Flink源码
3.1 任务提交流程
3.2 组件通信
3.3 任务调度
3.4 内存管理
1.Flink的运行架构
3)checkpoint
- checkpoint,保证了数据的一致性的同时 ,会不会对实时性有所影响,为什么?
一致性快照,这种方式保证了数据的一致性,但有一些潜在的问题:每次进行Checkpoint前,都需要暂停处理新流入数据,然后开始执行快照,假如状态比较大,一次快照可能长达几秒甚至几分钟。Checkpoint Barrier对齐时,必须等待所有上游通道都处理完,假如某个上游通道处理很慢,这可能造成整个数据流堵塞。针对这些问题Flink已经有了一些解决方案,并且还在不断优化。对于第一个问题,Flink提供了异步快照(Asynchronous Snapshot)的机制。当实际执行快照时,Flink可以立即向下广播Checkpoint Barrier,表示自己已经执行完自己部分的快照。同时,Flink启动一个后台线程,它创建本地状态的一份拷贝,这个线程用来将本地状态的拷贝同步到State Backend上,一旦数据同步完成,再给Checkpoint Coordinator发送确认信息。拷贝一份数据肯定占用更多内存,这时可以利用写入时复制(Copy-on-Write)的优化策略。Copy-on-Write指:如果这份内存数据没有任何修改,那没必要生成一份拷贝,只需要有一个指向这份数据的指针,通过指针将本地数据同步到State Backend上;如果这份内存数据有一些更新,那再去申请额外的内存空间并维护两份数据,一份是快照时的数据,一份是更新后的数据。对于第二个问题,Flink允许跳过对齐这一步,或者说一个算子子任务不需要等待所有上游通道的Checkpoint Barrier,直接将Checkpoint Barrier广播,执行快照并继续处理后续流入数据。为了保证数据一致性,Flink必须将那些较慢的数据流中的元素也一起快照,一旦重启,这些元素会被重新处理一遍。
4.Flink优化
4.1 资源配置调优
内存设置
部署on yarn,提交方式主要是yarn-per-job
提交参数 flink run -t per-job
Flink是实时流处理,关键在于资源情况能不能抗住高峰时期每秒的数据量,通常用QPS/TPS来描述数据情况。
tm:4G
jm:2G
并行度
--1)最优并行度
--先压测
测单个并行度的最大处理能力,再用总QPS/单并行度处理能力=并行度
实际并行度=并行度*1.2(不是完全准确。由于每条数据的字段不同、逻辑复杂程度不同的任务,最好富于一些)
--2)source并行度
单个topic:和分区数一致
多个topic:和分区数最大的topic一致
--3)transform并行度
key by前:和source保持一致
key by后:并发较大,建议设置并行度为 2 的整数次幂
--4)sink端并行度
Sink 端的数据量及下游的服务抗压能力进行评估
kafka:设置topic分区数
kudu:线程池、批写入
RocksDB大状态
--多目录
state.backend.rocksdb.localdir: /data1/flink/rocksdb,/data2/flink/rocksdb,/data3/flink/rocksdb
多磁盘多目录,有条件用SSD(固态硬盘)替换HDD机械硬盘
--增量检查点
state.backend.incremental:开启增量检查点,默认false,改为true。
--读缓存、刷写线程、写缓存|大小|数量|、合并
state.backend.rocksdb.block.cache-size: 整个 RocksDB 共享一个 block cache,读数据时内存的 cache 大小,该参数越大读数据时缓存命中率越高,默认大小为 8 MB,建议设置到 128 MB。
state.backend.rocksdb.thread.num: 用于后台 flush 和合并 sst 文件的线程数,默认为 1,建议调大,机械硬盘用户可以改为 4
state.backend.rocksdb.writebuffer.size: RocksDB 中,每个 State 使用一个 Column Family,每个 Column Family 使用独占的 write buffer,默认64MB。建议调大256M,调整这个参数通常要适当增加 L1 层的大小阈值max-size-level-bas
state.backend.rocksdb.writebuffer.count: 每个 Column Family 对应的 writebuffer 数目,默认值是 2,对于机械磁盘来说,如果内存⾜够大,可以调大到 5 左右
state.backend.rocksdb.writebuffer.number-to-merge: 将数据从 writebuffer 中 flush 到磁盘时,需要合并的 writebuffer 数量,默认值为 1,可以调成3
--本地恢复
state.backend.local-recovery: 设置本地恢复,当 Flink 任务失败时,可以基于本地的状态信息进行恢复任务,可能不需要从 hdfs 拉取数据
checkpoint
1.间隔周期 3min
2.最小暂停间隔 4min
3.超时时间 5min
4.保存
5.重启策略:1.固定延迟重启(重启间隔,重启次数)
6.ck语义
// 使⽤ RocksDBStateBackend 做为状态后端,并开启增量 Checkpoint
RocksDBStateBackend rocksDBStateBackend = new RocksDBStateBackend("hdfs://hadoop1:8020/flink/checkpoints", true);
env.setStateBackend(rocksDBStateBackend);
// 开启Checkpoint,间隔为 3 分钟
env.enableCheckpointing(TimeUnit.MINUTES.toMillis(3));
// 配置 Checkpoint
CheckpointConfig checkpointConf = env.getCheckpointConfig();
// 默认精准一次
checkpointConf.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE)
// 最小间隔 4 分钟
checkpointConf.setMinPauseBetweenCheckpoints(TimeUnit.MINUTES.toMillis(4))
// 超时时间 5 分钟
checkpointConf.setCheckpointTimeout(TimeUnit.MINUTES.toMillis(10));
// 保存checkpoint,手动取消也会保存ck
checkpointConf.enableExternalizedCheckpoints(
CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
4.2 反压
--反压原理
--定位
1.断开所有的算子链
2.查看WebUI每个算子的Backpressure情况
OK: 0 <= 比例 <= 0.10
LOW: 0.10 < 比例 <= 0.5
HIGH: 0.5 < 比例 <= 1
Task 的状态为 OK 表示没有反压,HIGH 表示这个 Task 被反压。
3.通过Metrics查看算子的输出入缓存池的使用率:inPoolUsage
当某个 Task 吞吐量下降时,基于 Credit 的反压机制,上游不会给该 Task 发送数据,所以该 Task 不会频繁卡在向 Buffer Pool 去申请 Buffer。反压监控实现原理就是监控 Task 是否卡在申请 buffer 这一步,所以遇到瓶颈的 Task 对应的反压⻚⾯必然会显示 OK,即表示没有受到反压。
inPoolUsage
反压时,可以看到遇到瓶颈的该Task的inPoolUage为1。不反压为0
--原因及处理
1.数据倾斜
2.系统资源
3.GC
通过GcView工具分析,提交任务指定jvm参数,从web ui下载日志,导入工具分析
4.3 数据倾斜
--keyby前
基本由于上游数据源kafka的分区数据不均匀导致的,Kafka 的 topic 中某些 partition 的数据量较大,某些 partition 的数据量较少,可以使用rebalance、shuffle来将数据均匀分配。
--keyby后:
LocalKeyBy的思想:本地攒批、聚合 => 自己实现
将上游数据积攒一个批次,对这些数据聚合后再发送到下游,类似于MR的combiner思想先预聚合。
//本地 buffer,存放 local 端缓存的 app 的 pv 信息
private HashMap<String, Long> localPvStat;
//缓存的数据量大小,即:缓存多少数据再向下游发送
private int batchSize;
//计数器,获取当前批次接收的数据量
private AtomicInteger currentSize;
public void flatMap(String in, Collector collector) throws Exception {
// 将新来的数据添加到 buffer 中
Long pv = localPvStat.getOrDefault(in, 0L);
localPvStat.put(in, pv + 1);
// 如果到达设定的批次,则将 buffer 中的数据发送到下游
if(currentSize.incrementAndGet() >= batchSize){
// 遍历 Buffer 中数据,发送到下游
for(Map.Entry<String, Long> appIdPv: localPvStat.entrySet()) {
collector.collect(Tuple2.of(appIdPv.getKey(), appIdPv.getValue()
}
// Buffer 清空,计数器清零
localPvStat.clear();
currentSize.set(0);
}
}
--keyby后:
两阶段聚合的方式
第一阶段聚合:key拼接随机数前缀或后缀,进行keyby、开窗、聚合
注意:聚合完不再是WindowedStream,要获取WindowEnd作为窗口标记作为第二阶段分组依据,避免不同窗口的结果聚合到一起)
第二阶段聚合:去掉随机数前缀或后缀,按照原来的key及windowEnd作keyby、聚合
4.4 kafka source调优
--动态发现kafka分区
当 Flink 任务运行过程中,如果被订阅的 topic 创建了新的 partition
FlinkKafkaConsumer 时,可以开启 partition 的动态发现,间隔多久检测一次是否有新创建的 partition
参数:间隔多久检测(多久检测一次)
--设置空闲等待
kafka某一个分区一段时间内未发送事件数据,意味着 WatermarkGenerator 也不会获得任何新数据去生成 watermark。
由于下游算子 watermark 的计算方式是取所有不同的上游并行数据源 watermark 的最小值,则其 watermark 将不会发生变化,导致窗口、定时器等不会被触发。
解决:
WatermarkStrategy 来检测空闲输入并将其标记为空闲状态。
WatermarkStrategy
.forBoundedOutOfOrderness(Duration.ofMinutes(2))
.withIdleness(Duration.ofMinutes(5))
--offset消费策略
group:默认消费策略,默认读取上次保存的offset信息,如果是应用第一次启动,读取不到上次的offset信息,则会根据这个参数auto.offset.reset的值来进行消费数据。建议使用这个。
earliest:从最早的数据开始进行消费,忽略存储的offset信息
latest:从最新的数据进行消费,忽略存储的offset信息
specific offset:从指定位置进行消费
timestamp:从topic中指定的时间点开始消费,指定时间点之前的数据忽略
--开启ck
当checkpoint机制开启的时候,KafkaConsumer会定期把kafka的offset信息还有其他operator的状态信息一块保存起来。当job失败重启的时候,Flink会从最近一次的checkpoint中进行恢复数据,重新从保存的offset消费kafka中的数据(也就是说,上面几种策略,只有第一次启动的时候起作用)。
为了能够使用支持容错的kafka Consumer,需要开启checkpoint