智联面试题

1.Flink的架构

[

JobManagers- 所谓Master ,负责协调分布式任务执行。 负责调度任务,协调checkpoint,协调故障恢复等。

There is always at least one Job Manager. A high-availability setup will have multiple JobManagers, one of which one is always the leader, and the others are standby. x

TaskManagers- 所谓slaves(工作节点/Worker),负责真正任务执行,执行一些Task(等价Spark Stage)下的subtask。负责流计算当中数据缓存或者数据shuffle.计算机节点连接JobManager汇报自身状态信息,并且告知主节点自己分配到任务的计算状态。

There must always be at least one TaskManager.

client - 主要是在任务计算之前将任务翻译成Dataflow Graph,将该Dataflow Graph提交给JobManagers。

Task - Flink会将任务通过Operator Chain的方式将一个任务划分为若干个Task,每个Task都有自己的并行度,根据设置并行度创建相应的subtask(线程)。通过Operator Chain可以减少线程-线程间通信成本和系统开销。

在这里插入图片描述

**Task Slots ** - 每个Task Slot代表TaskManager 计算资源子集。Task Slot可以均分TaskManager 的内存。比如说一个TaskManager 有3个Task Slot.则每个Task slot就代表1/3的内存空间。不同job的subtask之间可以通过Task Slot进行隔离。同一个Job的不同task的subtask可以共享Task slots。默认所有的subtask因为共享的是同一个资源组default,因此一个Job所需的Task Slots的数量就等于该Job下Task的最大并行度。

2.Flink和spark的不同之处

Spark DStream和Flink的区别?

  • Flink纯粹流计算框架(低延迟)、Spark Streaming 微批模拟流实现 (延迟高)

  • Flink所有的计算都是有状态的计算,无需人工干预。提供丰富数据模型存储状态。spark的状态计算太简陋了,只有mapwithState

  • Flink 支持状态的TTL(Time To Live)管理,但是Spark没有。

  • Flink支持基于ProcessingTime、Ingestion Time、EventTime 窗口计算 。Spark默认支持ProcessingTime(DStream)

  • Flink支持滑动、滚动、会话 、全局 ,spark仅仅支持 滑动和滚动。

  • Flink对窗口的控制更为便捷,提供触发器、Evictor

  • Flink提供了Watermarker,并且提出 乱序和迟到语义的处理,严格意义上讲Spark 无法处理迟到数据。

项目(job)部署的区别

  • Spark 支持 本地仿真、支持 spark-submit提交 ,不支持WebUI提交、一般不支持跨平台提交(实现SparkSubmit)
  • Flink 支持 本地仿真、支持flink run、支持webUI、支持远端提交。

3.HDFS的存储过程

  1. client端发送写文件请求,namenode检查文件是否存在,如果已存在,直接返回错误信息,否则,发送给client一些可用namenode节点;
  2. client将文件分块,并行存储到不同节点上datanode上,发送完成后,client同时发送信息给namenode和datanode;
  3. namenode收到的client信息后,发送确信信息给datanode;
  4. datanode同时收到namenode和datanode的确认信息后,提交写操作。

4.MapReduce的shuffle过程

在这里插入图片描述

Map的输出结果首先被缓存到内存,当缓存区(环状缓冲区)达到80% (默认大小为100MB),就会启动溢写操作,当前启动溢写操作时,首先把缓存中的数据进行分区,对每个分区的数据进行排序和合并。之后再写入到磁盘中,每次溢写 都会生成新的磁盘文件,随着Job执行,被溢写出到磁盘的文件会越来越多,在Map任务全部结束之前,这些溢写文件会被归并成一个大的磁盘文件,然后通知相应的Reduce任务来领取属于自己的数据。

Reduce任务从Map端的不用的Map机器领回属于自己的处理那部分数据,然后对数据进行处理

Map端:溢写

Reduce端:Fetch

5.Hadoop 切片的原理

1、block:block是物理切块,在文件上传到HDFS文件系统后,对大文将以每128MB的大小切分若干,存放在不同的DataNode上;

2、split:split是逻辑切片,在mapreduce中的map task开始之前,将文件按照指定的大小切割成若干个部分,每一部分称为一个split,默认是split的大小与block的大小相等,均为128MB。
注意:在hadoop1.x版本中,block默认的大小为64MB,在hadoop2.x版本修改成了128MB。

3、切片机制(将待处理数据执行逻辑切片(即按照一个特定切片大小,将待处理数据划分成逻辑上的多个split,然后每一个split分配一个map(mapTask)并行实例处理

4、map个数:由任务切片spilt决定的,默认情况下一个split的大小就是block参与任务的文件个数决定的)

文件的最后一个分片可能会超过128MB,由于常量SPLIT_SLOP = 1.1决定,大小范围在:0MB < lastSplit < 128+12.8 MB

6.Hive是什么?

“hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供简单的sql查询功能,可以将sql语句转换为MapReduce任务进行运行。

可以用来进行数据提取转化加载**(ETL)**,这是一种可以存储、查询和分析存储在Hadoop中的大规模数据的机制。

Hive并不适合那些需要低延迟的应用,如联机事务处理(OLTP)

7.使用flink时遇到的问题?

1、Kafka partition leader切换导致Flink重启

​ 答: 设置retries参数 关于producer参数设置,可以在Kafka的Partition发生leader切换时,Flink不重启,而是做3次尝试:

kafkaProducerConfig
         {
               "bootstrap.servers": "192.169.2.20:9093,192.169.2.21:9093,192.169.2.22:9093"
               "retries":3
         }

2、 Job运行中出现了OOM

​ 答: 说明保留的空间不够,这时需减少中间层的空间大小,通过配置降低taskmanager.memory.fraction的值来减少中间层的内存占比。该值表示Flink用于管理底层buffer所占用的内存比例。

3、 运行一段时间退出

​ 答:java.io.IOException:状态的大小大于允许的最大内存支持状态,考虑使用其他状态后端,例如文件系统状态后端。

状态存储,默认是在内存中,改为存储到HDFS中:

state.backend.fs.checkpointdir: hdfs://t-sha1-flk-01:9000/flink-checkpoints

8.使用spark时遇到的问题

1、 hadoopdatanode无法启动

答: NameNode和DataNode的namespaceID不一致 一般是由于两次或两次以上的格式化NameNode造成的,
有两种方法可以解决,
第一种方法是删除DataNode的所有资料(及将集群中每个datanode的/hdfs/data/current中的VERSION删掉,然后执行hadoopnamenode-format重启集群,错误消失。<推荐>);
第二种方法是修改每个DataNode的namespaceID(位于/hdfs/data/current/VERSION文件中)<优先>或修改NameNode的namespaceID(位于/hdfs/name/current/VERSION文件中),使其一致。

2、ERROR ActorSystemImpl: Uncaught fatal error from thread [sparkDriver-akka.remote.default-remote-dispatcher-8] shutting down ActorSystem [sparkDriver]

答:driver 内存不足。
提交spark-sumbit脚本时,--driver-memory 3g(自己指定大小) ,来相应的设置Driver内存。

3、 ERROR spark.MapOutputTrackerMasterActor: Map output statuses were 14371441 bytes which exceeds spark.akka.frameSize

分析:spark.akka.frameSize 是worker和driver通信的每块数据大小,控制Spark中通信消息的最大容量 (如 task 的输出结果),默认为10M。从错误中可以看出,我们的程序需要的实际大小是14M多,因此我们把这个参数调整到20M。(也可以从 worker的日志中进行排查。通常 worker 上的任务失败后,master 的运行日志上出现”Lost TID: “的提示,可通过查看失败的 worker 的日志文件($SPARK_HOME/worker/下面的log文件) 中记录的任务的 Serialized size of result 是否超过10M来确定。)

解决方案:提交spark-sumbit脚本时,--conf spark.akka.frameSize=20 (默认单位大小是M) ,来相应的设置Driver内存。

9.flink的checkpoint原理

checkpoint机制是Flink可靠性的基石,可以保证Flink集群在某个算子因为某些原因(如 异常退出)出现故障时,能够将整个应用流图的状态恢复到故障之前的某一状态,保 证应用流图状态的一致性。Flink的checkpoint机制原理来自“Chandy-Lamport algorithm”算法。 (分布式快照算)

每个需要checkpoint的应用在启动时,Flink的JobManager为其创建一个 CheckpointCoordinator,CheckpointCoordinator全权负责本应用的快照制作

  1. CheckpointCoordinator周期性的向该流应用的所有source算子发送barrier。
  2. 当某个source算子收到一个barrier时,便暂停数据处理过程,然后将自己的当前状 态制作成快照,并保存到指定的持久化存储中,最后向CheckpointCoordinator报告 自己快照制作情况,同时向自身所有下游算子广播该barrier,恢复数据处理
  3. 下游算子收到barrier之后,会暂停自己的数据处理过程,然后将自身的相关状态制作成快照,并保存到指定的持久化存储中,最后向CheckpointCoordinator报告自身 快照情况,同时向自身所有下游算子广播该barrier,恢复数据处理。
  4. 每个算子按照步骤3不断制作快照并向下游广播,直到最后barrier传递到sink算子,快照制作完成。
  5. 当CheckpointCoordinator收到所有算子的报告之后,认为该周期的快照制作成功; 否则,如果在规定的时间内没有收到所有算子的报告,则认为本周期快照制作失败 ;

在这里插入图片描述

barrier从Source Task处生成,一直流到Sink Task,期间所有的Task只要碰到barrier,就会触发自身进行快照;

CheckPoint barrier n-1处做的快照就是指Job从开始处理到 barrier n-1所有的状态数据;

barrier n 处做的快照就是指从Job开始到处理到 barrier n所有的状态数据;

https://www.jianshu.com/p/4d31d6cddc99

10.flink的异步io

Async I/O 是阿里巴巴贡献给社区的一个呼声非常高的特性,于1.2版本引入。主要目的是为了解决与外部系统交互时网络延迟成为了系统瓶颈的问题。流计算系统中经常需要与外部系统进行交互,比如需要查询外部数据库以关联上用户的额外信息。通常,我们的实现方式是向数据库发送用户a的查询请求,然后等待结果返回,在这之前,我们无法发送用户b的查询请求。这是一种同步访问的模式,如下图左边所示。

在这里插入图片描述

图中棕色的长条表示等待时间,可以发现网络等待时间极大地阻碍了吞吐和延迟。为了解决同步访问的问题,异步模式可以并发地处理多个请求和回复。也就是说,你可以连续地向数据库发送用户a、b、c等的请求,与此同时,哪个请求的回复先返回了就处理哪个回复,从而连续的请求之间不需要阻塞等待,如上图右边所示。这也正是 Async I/O 的实现原理。

我们需要自定义一个类实现RichAsyncFunction这个抽象类,实现其中的抽象方法, 然后在asyncInvoke()使用CompletableFuture执行异步操作

AsyncDataStream 有两个静态方法,orderedWait 和 unorderedWait,对应了两种输出模式:有序和无序。

  • 有序:消息的发送顺序与接受到的顺序相同(包括 watermark ),也就是先进先出。
  • 无序:
    • 在 ProcessingTime 的情况下,完全无序,先返回的结果先发送。
    • 在 EventTime 的情况下,watermark 不能超越消息,消息也不能超越 watermark,也就是说 watermark 定义的顺序的边界。在两个 watermark 之间的消息的发送是无序的,但是在watermark之后的消息不能先于该watermark之前的消息发送。

有序: 有序比较简单,使用一个队列就能实现。所有新进入该算子的元素(包括 watermark),都会包装成 Promise 并按到达顺序放入该队列。如下图所示,尽管P4的结果先返回,但并不会发送,只有 P1 (队首)的结果返回了才会触发 Emitter 拉取队首元素进行发送。在这里插入图片描述

ProcessingTime 无序: 使用两个队列就能实现,一个 uncompletedQueue 一个 completedQueue。所有新进入该算子的元素,同样的包装成 Promise 并放入 uncompletedQueue 队列,当uncompletedQueue队列中任意的Promise返回了数据,则将该 Promise 移到 completedQueue 队列中,并通知 Emitter 消费。如下图所示:

在这里插入图片描述

EventTime 无序:EventTime 无序类似于有序与 ProcessingTime 无序的结合体。因为有 watermark,需要协调 watermark 与消息之间的顺序性,所以uncompletedQueue中存放的元素从原先的 Promise 变成了 Promise 集合。如果进入算子的是消息元素,则会包装成 Promise 放入队尾的集合中。如果进入算子的是 watermark,也会包装成 Promise 并放到一个独立的集合中,再将该集合加入到 uncompletedQueue 队尾,最后再创建一个空集合加到 uncompletedQueue 队尾。这样,watermark 就成了消息顺序的边界。只有处在队首的集合中的 Promise 返回了数据,才能将该 Promise 移到 completedQueue 队列中,由 Emitter 消费发往下游。只有队首集合空了,才能处理第二个集合。这样就保证了当且仅当某个 watermark 之前所有的消息都已经被发送了,该 watermark 才能被发送。过程如下图所示:
在这里插入图片描述

11.flink的实时去重

12.flink的时间概念有哪些?各有什么不同?

  • 事件生成时间 ( EventTime )
  • 事件接入时间 (Ingestion time) 事件进入到Flink Dataflow的时间
  • 事件处理时间 (Processing time)

默认Flink使用的是ProcessingTime ,因此一般情况下如果用户需要使用 Event time/Ingestion time需要设置时间属性

val fsEnv = StreamExecutionEnvironment.getExecutionEnvironment
fsEnv.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
//window  操作
fsEnv.execute("event time")

13.介绍一下flink的窗口计算

通过使用窗口对无限的流数据划分成固定大小的 buckets,然后基于落入同一个bucket(窗口)中的元素执行计算。Flink将窗口计算分为两大类。

一类基于keyed-stream窗口计算。

stream
       .keyBy(...)               <-  分组
       .window(...)              <-  必须: "assigner" 窗口分配器
      [.trigger(...)]            <-  可选: "trigger" 每一种类型的窗口系统都有默认触发器
      [.evictor(...)]            <-  可选: "evictor" 可以剔除窗口中元素
      [.allowedLateness(...)]    <-  可选: "lateness" 可以处理迟到数据
      [.sideOutputLateData(...)] <-  可选: "output tag" 可以Side Out获取迟到的元素
       .reduce/aggregate/fold/apply()      <-  必须: "function"
      [.getSideOutput(...)]      <-  可选: 获取Sideout数据 例如迟到数据

直接对non-keyed Stream窗口计算

stream
       .windowAll(...)           <-  required: "assigner"
      [.trigger(...)]            <-  optional: "trigger" (else default trigger)
      [.evictor(...)]            <-  optional: "evictor" (else no evictor)
      [.allowedLateness(...)]    <-  optional: "lateness" (else zero)
      [.sideOutputLateData(...)] <-  optional: "output tag" (else no side output for late data)
       .reduce/aggregate/fold/apply()      <-  required: "function"
      [.getSideOutput(...)]      <-  optional: "output tag"

一旦应属于该窗口的第一个元素到达,就会创建一个窗口,并且当时间|WaterMarker(Event Tme或Process Time)超过其Window End 时间加上用户指定的允许延迟时,该窗口将被完全删除。

  • Tumbling Windows :滚动,窗口长度和滑动间隔相等,窗口之间没有重叠。(时间)
dataStream.flatMap(_.split("\\s+"))
    .map((_,1))
    .keyBy(0)
    .window(TumblingProcessingTimeWindows.of(Time.seconds(5)))
    .reduce((v1,v2)=>(v1._1,v1._2+v2._2))
    .print()
  • Sliding Windows:滑动,窗口长度 大于 滑动间隔,窗口之间存在数据重叠。(时间)
dataStream.flatMap(_.split("\\s+"))
    .map((_,1))
    .keyBy(0)
    .window(SlidingProcessingTimeWindows.of(Time.seconds(4),Time.seconds(2)))
    .fold(("",0))((z,v)=>(v._1,z._2+v._2))
    .print()
  • Session Windows: 会话窗口,窗口没有固定大小,每个元素都会形成一个新窗口,如果窗口的间隔小于指定时间,这些窗口会进行合并。(时间)
dataStream.flatMap(_.split("\\s+"))
.map((_,1))
.keyBy(0)
.window(ProcessingTimeSessionWindows.withGap(Time.seconds(5)))
.aggregate(new AggregateFunction[(String,Int),(String,Int),(String,Int)] {
    override def createAccumulator(): (String, Int) = {
        ("",0)
    }

    override def add(value: (String, Int), accumulator: (String, Int)): (String, Int) = {
        (value._1,value._2+accumulator._2)
    }

    override def getResult(accumulator: (String, Int)): (String, Int) = {
        accumulator
    }

    override def merge(a: (String, Int), b: (String, Int)): (String, Int) = {
        (a._1,a._2+b._2)
    }
})
.print()
  • Global Windows:全局窗口,窗口并不是基于时间划分窗口,因此不存在窗口长度和时间概念。需要用户定制触发策略,窗口才会触发。
dataStream.flatMap(_.split("\\s+"))
.map((_,1))
.keyBy(_._1)
.window(GlobalWindows.create())
.trigger(CountTrigger.of(4))
.apply(new WindowFunction[(String,Int),(String,Int),String, GlobalWindow] {
    override def apply(key: String, window: GlobalWindow, inputs: Iterable[(String, Int)],
                       out: Collector[(String, Int)]): Unit = {
        println("key:"+key+" w:"+window)
        inputs.foreach(t=>println(t))
        out.collect((key,inputs.map(_._2).sum))
    }
})
.print()

WINDOW FUNCTION:定义Window Assigners后,我们需要指定要在每个窗口上执行的计算。 这是Window Function的职责,一旦系统确定某个窗口已准备好进行处理,该Window Function将用于处理每个窗口的元素。Flink提供了以下Window Function处理函数:

  • ReduceFunction
  • AggregateFunction
  • FoldFunction(废弃)
    • 不能用在Merger window中,不可用在SessionWindows中。
  • apply/WindowFunction(旧版-一般不推荐)
    • 可以获取窗口的中的所有元素,并且可以拿到一些元数据信息,无法操作窗口状态。
  • ProcessWindowFunction(重点掌握)
    • 可以获取窗口的中的所有元素,并且拿到一些元数据信息。是WindowFunction的替代方案,因为该接口可以直接操作窗口的State|全局State

Trigger:Trigger定义了何时开始使用窗口计算函数计算窗口。每个窗口分配器都会有一个默认的Trigger。如果,默认的Trigger不能满足你的需求,你可以指定一个自定义的trigger().

trigger接口有五个方法允许trigger对不同的事件做出反应:

  • onElement():进入窗口的每个元素都会调用该方法。
  • onEventTime():事件时间timer触发的时候被调用。
  • onProcessingTime():处理时间timer触发的时候会被调用。
  • onMerge():有状态的触发器相关,并在它们相应的窗口合并时合并两个触发器的状态,例如使用会话窗口。
  • clear():该方法主要是执行窗口的删除操作。

注意:

  1. 前三方法决定着如何通过返回一个TriggerResult来操作输入事件。
  • CONTINUE:什么都不做。
  • FIRE:触发计算。
  • PURE:清除窗口的元素。
  • FIRE_AND_PURE:触发计算和清除窗口元素。
  1. .这些方法中的任何一个都可用于为将来的操作注册处理或事件时间计时器 , 内置和自定义触发器

Flink内部有一些内置的触发器:

  • EventTimeTrigger:基于事件时间和watermark机制来对窗口进行触发计算。
  • ProcessingTimeTrigger:基于处理时间触发。
  • CountTrigger:窗口元素数超过预先给定的限制值的话会触发计算。
  • PurgingTrigger作为其它trigger的参数,将其转化为一个purging触发器。

14.flink的规模是多少?是集群还是本地的?job是以什么方式提交的?

Flink 支持 本地仿真(测试开发)、支持flink run(yarn平台)、支持webUI、支持远端提交。

15.flink是怎么处理迟到数据的

waterMark=数据的最大事件时间-允许乱序时间值

则只要waterMark<窗口的endTime+延迟时间,并且属于该窗口,就能触发window操作。

设置允许延迟的时间是通过allowedLateness(lateness: Time)设置

保存延迟数据则是通过sideOutputLateData(outputTag: OutputTag[T])保存

获取延迟数据是通过DataStream.getSideOutput(tag: OutputTag[X])获取

16.spark的状态管理

即 sparkstreaming带状态的操作,updateStateByKey和mapWithState这两个方法

  • UpdateStateByKey

    该updateStateByKey操作允许您在使用新信息持续更新时保持任意状态。要使用它,您必须执行两个步骤。

    • 定义状态 - 状态可以是任意数据类型。

    • 定义状态更新功能 - 使用函数指定如何使用先前状态和输入流中的新值更新状态 。

    在每个批处理中,Spark都会对所有现有密钥应用状态更新功能,无论它们是否在批处理中都有 新数据。如果更新函数返回,None则将删除键值对。

  • mapWithSate

    也是用于全局统计key的状态,但是它如果没有数据输入,便不会返回之前的key的状态,有一点增量的感觉。效率更高。

    建议使用mapWithState,因为他不用全量的更新,只用更新增量的,所以效率要比updateStateByKey高很多,这两个方法都必须开启chckpoint,因为他们需要把状态保存在checkpoint里面。


17.spark怎么保证精准一次

Spark Streaming+Kafka提交offset实现有且仅有一次(exactly-once)

  • auto.offset.reset设置为earliest,即当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始,这样设置的目的是为了一开始可以获取到kafka对应主题下的所有的历史消息。
  • enable.auto.commit 设置为false,如果是true,则这个消费者的偏移量会在后台自动提交,这样设置目的是为了后面自己提交offset,因为如果虽然获取到了消息,但是后面的转化操作并将结果写到如hive中并没有完成程序就挂了的话,这样是不能将这次的offset提交的,这样就可以等程序重启之后接着上次失败的地方继续消费
  • group.id 是不能变得,也就是offset是和topic和group绑定的,如果换一个group的话,程序将从头消费所有的历史数据

关于offset过期时间: kafka offset默认的过期时间是一天,当上面的程序挂掉,一天之内没有重启,也就是一天之内没有保存新的offset的话,那么之前的offset就会被删除,再重启程序,就会从头开始消费kafka里的所有历史数据,这种情况是有问题的,所以可以通过设置offsets.retention.minutes自定义offset过期时间,该设置单位为分钟,默认为1440。

18.kafka的规模,部署形式

部署形式:

  • 单broker模式
  • 单机多broker模式 (伪集群)
  • 多机多broker模式 (真正的集群模式)

成程序就挂了的话,这样是不能将这次的offset提交的,这样就可以等程序重启之后接着上次失败的地方继续消费

  • group.id 是不能变得,也就是offset是和topic和group绑定的,如果换一个group的话,程序将从头消费所有的历史数据

关于offset过期时间: kafka offset默认的过期时间是一天,当上面的程序挂掉,一天之内没有重启,也就是一天之内没有保存新的offset的话,那么之前的offset就会被删除,再重启程序,就会从头开始消费kafka里的所有历史数据,这种情况是有问题的,所以可以通过设置offsets.retention.minutes自定义offset过期时间,该设置单位为分钟,默认为1440。

18.kafka的规模,部署形式

部署形式:

  • 单broker模式
  • 单机多broker模式 (伪集群)
  • 多机多broker模式 (真正的集群模式)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值