最近总结一波面试问题(包括python,MySQL,数据科学,机器学习,大数据等,一个人力量有限),有兴趣查看 github
1.hadoop 和 spark 使用场景?
Hadoop/MapReduce 和 Spark 最适合的都是做离线型的数据分析,但 Hadoop 特别适合是单次分析的数据量“很大”的情景,而 Spark 则适用于数据量不是很大的情景。
(1) 一般情况下,对于中小互联网和企业级的大数据应用而言,单次分析的数量都不会“很大”,因此可以优先考虑使用 Spark。
(2) 业务通常认为 Spark 更适用于机器学习之类的“迭代式”应用,80GB 的压缩数据(解压后超过 200GB),10 个节点的集群规模,跑类似“sum+group-by”的应用,MapReduce 花了 5分钟,而 spark 只需要 2 分钟
2. python的pandas分析和spark数据处理有什么区别?
| python单机 | pyspark集群 |
基本数据结构 | ndarray、Series、DataFrame | RDD |
并行处理流程 | 线程、进程、协程 | Parallelize |
计数 / 汇总 | count/sum/groupby | count/sum/groupBy |
机器学习 | sklearn、scipy | mlib |
3.spark 如何保证宕机迅速恢复?
(1) 适当增加 spark standby master
(2)编写 shell 脚本,定期检测 master 状态,出现宕机后对 master 进行重启操作
4. hadoop 和 spark 的相同点和不同
(1) Hadoop 底层使用 MapReduce 计算架构,只有 map 和 reduce 两种操作,表达能力比较欠缺,而且在 MR 过程中会重复的读写 hdfs,造成大量的磁盘 io 读写操作,所以适合高时延环境下批处理计算的应用;
(2) Spark 是基于内存的分布式计算架构,提供更加丰富的数据集操作类型,主要分成转化操作和行动操作,包括 map、reduce、filter、flatmap、groupbykey、reducebykey、union 和join 等,数据分析更加快速,所以适合低时延环境下计算的应用;
(3) spark 与 hadoop 最大的区别在于迭代式计算模型。基于 mapreduce 框架的 Hadoop 主要分为 map 和 reduce 两个阶段,两个阶段完了就结束了,所以在一个 job 里面能做的处理很有限;spark 计算模型是基于内存的迭代式计算模型,可以分为 n 个阶段,根据用户编写的RDD 算子和程序,在处理完一个阶段后可以继续往下处理很多个阶段,而不只是两个阶段。
所以 spark 相较于 mapreduce,计算模型更加灵活,可以提供更强大的功能。但是 spark 也有劣势,由于 spark 基于内存进行计算,虽然开发容易,但是真正面对大数据的时候,在没有进行调优的轻局昂下,可能会出现各种各样的问题,比如 OOM 内存溢
出等情况,导致 spark 程序可能无法运行起来,而 mapreduce 虽然运行缓慢,但是至少可以慢慢运行完。
5. checkpoint 检查点机制
应用场景:当 spark 应用程序特别复杂,从初始的 RDD 开始到最后整个应用程序完成有很多的步骤,而且整个应用运行时间特别长,这种情况下就比较适合使用 checkpoint 功能。
原因:对于特别复杂的 Spark 应用,会出现某个反复使用的 RDD,即使之前持久化过但由于节点的故障导致数据丢失了,没有容错机制,所以需要重新计算一次数据。
Checkpoint 首先会调用 SparkContext 的 setCheckPointDIR()方法,设置一个容错的文件系统的目录,比如说 HDFS;然后对 RDD 调用 checkpoint()方法。之后在 RDD 所处的 job运行结束之后,会启动一个单独的 job,来将 checkpoint 过的 RDD 数据写入之前设置的文件系统,进行高可用、容错的类持久化操作。
检查点机制是我们在 spark streaming 中用来保障容错性的主要机制,它可以使 spark streaming 阶段性的把应用数据存储到诸如 HDFS 等可靠存储系统中,以供恢复时使用。具体来说基于以下两个目的服务:
控制发生失败时需要重算的状态数。Spark streaming 可以通过转化图的谱系图来重算状态,检查点机制则可以控制需要在转化图中回溯多远。
提供驱动器程序容错。如果流计算应用中的驱动器程序崩溃了,你可以重启驱动器程序并让驱动器程序从检查点恢复,这样 spark streaming 就可以读取之前运行的程序处理数据的进度,并从那里继续。
6. checkpoint 和持久化机制的区别
最主要的区别在于持久化只是将数据保存在 BlockManager 中,但是 RDD 的 lineage(血缘关系,依赖关系)是不变的。但是 checkpoint 执行完之后,rdd 已经没有之前所谓的依赖 rdd。了,而只有一个强行为其设置的 checkpointRDD,checkpoint 之后 rdd 的 lineage 就改变了。持久化的数据丢失的可能性更大,因为节点的故障会导致磁盘、内存的数据丢失。但是checkpoint 的数据通常是保存在高可用的文件系统中,比如 HDFS 中,所以数据丢失可能性比较低。
7. RDD 机制
rdd 分布式弹性数据集,简单的理解成一种数据结构,是 spark 框架上的通用货币。 所有算子都是基于 rdd 来执行的,不同的场景会有不同的 rdd 实现类,但是都可以进行互相转换。rdd 执行过程中会形成 dag 图,然后形成 lineage 保证容错性等。从物理的角度来看 rdd存储的是 block 和 node 之间的映射。
RDD 是 spark 提供的核心抽象,全称为弹性分布式数据集。
RDD 在逻辑上是一个 hdfs 文件,在抽象上是一种元素集合,包含了数据。它是被分区的,分为多个分区,每个分区分布在集群中的不同结点上,从而让 RDD 中的数据可以被并行操作(分布式数据集)比如有个 RDD 有 90W 数据,3 个 partition,则每个分区上有 30W 数据。RDD 通常通过 Hadoop 上的文件,即 HDFS 或者 HIVE 表来创建,还可以通过应用程序中的集合来创建;
RDD 最重要的特性就是容错性,可以自动从节点失败中恢复过来。即如果某个结点上的 RDD partition 因为节点故障,导致数据丢失,那么 RDD 可以通过自己的数据来源重新计算该 partition。
这一切对使用者都是透明的 RDD 的数据默认存放在内存中,但是当内存资源不足时,spark 会自动将 RDD 数据写入磁盘。比如某结点内存只能处理 20W 数据,那么这 20W 数据就会放入内存中计算,剩下 10W 放到磁盘中。RDD 的弹性体现在于 RDD 上自动进行内存和磁盘之间权衡和切换的机制。
8. RDD 持久化原理?
spark 非常重要的一个功能特性就是可以将 RDD 持久化在内存中。调用 cache()和 persist()方法即可。cache()和 persist()的区别在于,cache()是 persist()的一种简化方式,cache()的底层就是调用 persist()的无参版本 persist(MEMORY_ONLY),将数据持久化到内存中。如果从内存中清除缓存,可以使用 unpersist()方法。
RDD 持久化是可以手动选择不同的策略的。在调用 persist()时传入对应的 StorageLevel即可。对于scala调用和python调用还有点区别,有些参数python不支持,但大部分还是够用了。
(1)MEMORY_ONLY: 以非序列化的 Java 对象的方式持久化在 JVM 内存中。如果内存无法完全存储 RDD 所有的 partition,那么那些没有持久化的 partition 就会在下一次需要使用它的时候,被重新计算。
(2)MEMORY_AND_DISK: 同上,但是当某些 partition 无法存储在内存中时,会持久化到磁盘中。下次需要使用这些 partition 时,需要从磁盘上读取。
(3)MEMORY_ONLY_SER: 同 MEMORY_ONLY,但是会使用 Java 序列化方式,将 Java 对象序列化后进行持久化。可以减少内存开销,但是需要进行反序列化,因此会加大 CPU 开销。
(4)MEMORY_AND_DSK_SER: 同 MEMORY_AND_DSK,但是使用序列化方式持久化 Java对象。
(5)DISK_ONLY: 使用非序列化 Java 对象的方式持久化,完全存储到磁盘上。
(6)MEMORY_ONLY_2/MEMERY_AND_DISK_2:如果是尾部加了 2 的持久化级别,表示会将持久化数据复用一份,保存到其他节点,从而在数据丢失时,不需要再次计算,只需要使用备份数据即可。
9. Spark 工作的一个流程
提交任务:
用户提交一个任务。 入口是从 SparkContext(sc) 开始的。 sc 会去创建一个DAGSchedule和 TaskScheduler。根据不同的提交模式,TaskScheduler的实现有所区别,而DAGSchedule与运行模式无关。DAGScheduler 会根据 RDD 的宽依赖或者窄依赖,进行阶段的划分。划分好后放入 taskset 中,交给 taskscheduler 。appclient 会到 master 上注册。首先会去判断数据本地化,尽量选最好的本地化模式去执行。打散 Executor 选择相应的 Executor 去执行。ExecutorRunner 会去创建CoarseGrainerExecutorBackend 进程。 通过线程池的方式去执行任务。
反向:
Executor 向 SchedulerBackend 反向注册
Spark On Yarn 模式下。 driver 负责计算调度。appmaster 负责资源的申请。
10. spark 核心编程原理
(1) 定义初始的 RDD,即第一个 RDD 是从哪里来,读取数据,包括 hdfs、linux 本地文件、程序中的集合;
(2)定义对 RDD 的计算操作,这在 spark 中称为算子,转换操作和行动操作,包括 map、reduce、flatmap、reducebykey 等,比 mapreduce 提供的 map 和 reduce 强大的太多;
(3) 就是循环往复迭代的过程。第一个计算完了以后,数据可能到了新的一批结点上,变成了一个新的 RDD。然后再次反复,针对新的 RDD 定义计算操作;
(4)获得最终的数据,将保存起来。
11. spark 基本工作原理?
client端: 我们在本地编写了 spark 程序,必须在某台能够连接 spark 集群的机器上提交该 spark程序;
spark 集群:将 spark 程序提交到 spark 集群上进行运行。
12. spark 有哪些组件?
(1)master:管理集群和节点,不参与计算。
(2)worker:计算节点,进程本身不参与计算,和 master 汇报。
(3)Driver:运行程序的 main 方法,创建 spark context 对象。
(4)spark context:控制整个 application 的生命周期,包括 dagsheduler 和 task scheduler 等组件。
(5)client:用户提交程序的入口。
13. spark 工作机制?
用户在 client 端提交作业后,会由 Driver 运行 main 方法并创建 spark context 上下文。执行 add 算子,形成 dag 图输入 dagscheduler,按照 add 之间的依赖关系划分 stage 输入 task scheduler。 task scheduler 会将 stage 划分为 task set 分发到各个节点的 executor 中执行。
14. Spark 的三种提交模式是什么?
(1)Spark 内核架构,即 standalone 模式,基于 Spark 自己的 Master-Worker 集群;
(2)基于 Yarn 的 yarn-cluster 模式;
(3)基于 Yarn 的 yarn-client 模式。
如果你要切换到第二种和第三种模式,将之前提交 spark 应用程序的spark-submit 脚本,加上--master 参数,设置为 yarn-cluster,或 yarn-client 即可。如果没设置就是 standalone 模式。
15. 宽依赖和窄依赖
宽依赖:shuffle dependency,本质就是 shuffle。父 RDD 的每一个 partition 中的数据,都可能会传输一部分到下一个子 RDD 的每一个 partition 中,此时会出现父 RDD 和子 RDD的 partition 之间具有交互错综复杂的关系,这种情况就叫做两个 RDD 之间是宽依赖。
窄依赖:narrow dependency,父 RDD 和子 RDD 的 partition 之间的对应关系是一对一。
16. Spark yarn-cluster 架构?
Yarn-cluster 用于生产环境,优点在于 driver 运行在 NM,没有网卡流量激增的问题。缺点在于调试不方便,本地用 spark-submit 提交后,看不到 log,只能通过 yarm application-logs application_id 这种命令来查看,很麻烦。
(1)将 spark 程序通过 spark-submit 命令提交,会发送请求到 RM(相当于 Master),请求启动 AM;
(2)在 yarn 集群上,RM 会分配一个 container,在某个 NM 上启动 AM;
(3)在 NM 上会启动 AM(相当于 Driver),AM 会找 RM 请求 container,启动executor;
(4)RM 会分配一批 container 用于启动 executor;
(5)AM 会连接其他 NM(相当于 worker),来启动 executor;(6)executor 启动后,会反向注册到 AM。
17. Spark yarn-client 架构?
Yarn-client 用于测试,因为 driver 运行在本地客户端,负责调度 application,会与 yarn 集群产生大量的网络通信,从而导致网卡流量激增,可能会被公司的SA 警告。好处在于,直接执行时本地可以看到所有的 log,方便调试。
(1)将 spark 程序通过 spark-submit 命令提交,会发送请求到 RM,请求启动 AM;
(2)在 yarn 集群上,RM 会分配一个 container 在某个 NM 上启动 application;
(3)在 NM 上会启动 application master,但是这里的 AM 其实只是一个ExecutorLauncher,功能很有限,只会去申请资源。AM 会找 RM 申请 container,启动 executor;
(4)RM 会分配一批 container 用于启动 executor;
(5)AM 会连接其他 NM(相当于 worker),用 container 的资源来启动 executor;(6)executor 启动后,会反向注册到本地的 Driver 进程。通过本地的 Driver 去执行DAGsheduler 和 Taskscheduler 等资源调度。和 Spark yarn-cluster 的区别在于,cluster 模式会在某一个 NM 上启动 AM 作为 Driver。
18. spark 内核架构原理
Master 进程:主要负责资源的调度和分配,还有集群的监控等职责。
Driver 进程:我们编写的 spark 程序就在 Driver 上由 Driver 进程执行。
Worker 进程:主要负责两个,第一个是用自己的内存去存储 RDD 的某个或者某些partition,第二个是启动其他进程和线程,对 RDD 上的 partition 进行并行的处理和计算。
Executor 和 Task:负责执行对 RDD 的 partition 进行并行的计算,也就是执行我们对 RDD定义的各种算子。
(1) 将 spark 程序通过 spark-submit(shell)命令提交到结点上执行,其实会通过反射的方式,创建和构造一个 DriverActor 进程出来,通过 Driver 进程执行我们的 Application 应用程序。
(2) 应用程序的第一行一般是先构造 SparkConf,再构造 SparkContext
(3)SparkContext 在初始化的时候,做的最重要的两件事就是构造 DAGScheduler 和TaskScheduler;
(4)TaskScheduler 会通过对应的一个后台进程去连接 Master,向 Master 注册 Application;
(5)Master 通知 Worker 启动 executor;
(6)executor 启动之后会自己反向注册到 TaskSchduler,所有的 executor 都反向注册到 Driver之后,Driver 结束 SparkContext 初始化,然后继续执行自己编写的代码;
(7)每执行一个 action 就会创建一个 job;
(8)DAGScheduler 会根据 Stage 划分算法,将 job 划分为多个 stage,并且为每个 stage 创建TaskSet(里面有多个 task);
(9)TaskScheduler 会根据 task 分配算法,把 TaskSet 里面每一个 task 提交到 executor 上执行;
(10)executor 每接收到一个 task,都会用 taskRunner 来封装 task,然后从线程池取出一程,执行这个 task;
(11)taskRunner 将我们编写的代码(算子及函数)进行拷贝,反序列化,然后执行 task;
(12)task 有两种,shuffleMapTask 和 resultTask,只有最后一个 stage 是 resultTask,之前的都是 shuffleMapTask;
(13)所以最后整个 spark 应用程序的执行,就是 job 划分为 stage,然后 stage 分批次作为 taskset提交到 executor 执行,每个 task 针对 RDD 的一个 partition 执行我们定义的算子和函数,以此类推,直到所有的操作执行完毕。
19.spark streaming 中有状态转化操作
DStream 的有状态转化操作是跨时间区间跟踪数据的操作,即一些先前批次的数据也被用来在新的批次中计算结果。主要包括滑动窗口和 updateStateByKey(),前者以一个时间阶段为滑动窗口进行操作,后者则用来跟踪每个键的状态变化。(例如构建一个代表用户会话的对象)。
有状态转化操作需要在你的 Streaming Context 中打开检查点机制来确保容错性。
滑动窗口:基于窗口的操作会在一个比 Streaming Context 的批次间隔更长的时间范围内,通过整合多个批次的结果,计算出整个窗口的结果。基于窗口的操作需要两个参数,分别为窗口时长以及滑动时长,两者都必须是 Streaming Context 的批次间隔的整数倍。窗口时长控制每次计算最近的多少个批次的数据,滑动步长控制对新的 DStream 进行计算的间隔。
最简单的窗口操作是 window(),它返回的 DStream 中的每个 RDD 会包含多个批次中的户数,可以分别进行其他 transform()操作。在轨迹异常项目中,duration 设置为 15s,窗口函数设置为kafkadstream.windo(Durations.seconds(600),Durations.seconds(600));
updateStateByKey 转化操作:需要在 DStream 中跨批次维护状态(例如跟踪用户访问网站的会话)。针对这种情况,用于键值对形式的 DStream。给定一个由(键、事件)对构成的DStream,并传递一个指定根据新的事件更新每个键对应状态的函数。
举例:在网络服务器日志中,事件可能是对网站的访问,此时键是用户的 ID。使用UpdateStateByKey()可以跟踪每个用户最近访问的 10 个页面。这个列表就是“状态”对象我们会在每个事件到来时更新这个状态。
20. DStream 以及基本工作原理
DStream 是 spark streaming 提供的一种高级抽象,代表了一个持续不断的数据流。DStream 可以通过输入数据源来创建,比如 Kafka、flume 等,也可以通过其他 DStream 的高阶函数来创建,比如 map、reduce、join 和 window 等。DStream 内部其实不断产生 RDD,每个 RDD 包含了一个时间段的数据。Spark streaming 一定是有一个输入的 DStream 接收数据,按照时间划分成一个一个的batch,并转化为一个 RDD,RDD 的数据是分散在各个子节点的 partition 中。