目标
阅读完本文,你讲对 Hadoop,Spark 有个简单认识,并学习到 Spark 中的一些基础概念。
背景
因为业务需求,工程方需要将大量的 hive 数据导入缓存集群中,由于数据量比较大,MapReduce 计算模型已经不能满足我们的需求,于是决定使用 Spark 代替 MapReduce对数据进行处理。
但是在使用的过程中发现,功能是实现了,但是1亿的数据量要导2~3个小时,那还搞个线啊。根本原因还是对 Spark 的运行原理理解不深入,只停留在了表层使用,要对 Spark 进行 性能优化那是必须的,于是就有了今天的这篇入门文章。
讲一下,Hadoop
在早期的 Hadoop 中采用的是 MRv1版本的 MapReduce 编程模型。MRv1包括三个部分内容:
1. 运行时环境(JobTracker 和 TaskTracker)
2. 编程模型(MapReduce)
3. 数据处理引擎(Map 任务和 Reduce 任务)
MRv1的简单示意图如下:
具体一点就是(画图不易):
从上图中可以看出 MRv1的流程与设计思路:
1. 首先用户程序 (JobClient) 提交了一个 job,job 的信息会发送到 Job Tracker 中,Job Tracker 是 Map-reduce 框架的中心,他需要与集群中的机器定时通信 (heartbeat), 需要管理哪些程序应该跑在哪些机器上,需要管理所有 job 失败、重启等操作。
2. TaskTracker 是 Map-reduce 集群中每台机器都有的一个部分,他做的事情主要是监视自己所在机器的资源情况。
3. TaskTracker 同时监视当前机器的 tasks 运行状况。TaskTracker 需要把这些信息通过 heartbeat 发送给 JobTracker,JobTracker 会搜集这些信息以给新提交的 job 分配运行在哪些机器上。上图虚线箭头就是表示消息的发送 - 接收的过程。
MRv1 存在一些不足:
1. 可扩展性差,JobTracker 职责过重,它即负责资源管理有负责任务调度,当集群繁忙时,JobTracker 可能成为系统瓶颈,也增加了 JobTracker fail 的风险,业界普遍总结出 MRv1只能支持4000节点主机的上线。
2. 可用性差,JobTracker 是 Map-reduce 的集中处理点,存在单点故障。
3. 在 TaskTracker 端,以 map/reduce task 的数目作为资源的表示过于简单,没有考虑到 cpu/ 内存的占用情况,如果两个大内存消耗的 task 被调度到了一块,很容易出现 OOM。
4. 资源利用率低,TaskTracker 使用 slot 等量划分节点上的资源量。啥意思,假设资源量总数的 slot 数为 n,task 必须获取到 slot 后才能有机会运行,如果某些 task 没有充分利用 slot,然而其他需要大量计算资源的 task 也无法获取这些空闲的资源。同时 slot 还分为 Map Slot 和 Reduce Slot 两种,作业刚启动的时候大部分的应该是 MapTask,此时 Reduce Task 都还没有调度,Reduce Slot 的资源就闲置了。
5. 不能支持多种 MapReduce 框架,无法通过可插拔方式将自身的 MapReduce 框架替换为其他实现,比如 Spark、Strom等。
技术在进步,分布式系统也在不断的变化,MRv1的框架已经不符合当前的要求了,从 0.23.0 版本开始,Hadoop 的 MapReduce 框架完全重构,发生了根本的变化。新的 Hadoop MapReduce 框架命名为 MRv2 或者叫 Yarn,Yarn 的架构图如下:
MRv2重用了 MRv1中的编程模型和数据处理引擎。但是运行时环境被重构了,JobTracker 被拆分成通用的资源调度平台ResourceManager(简称 RM),节点管理器NodeManager 和负责各个计算框架的任务调度模型 ApplicationMaster(简称 AM)。
ResourceManager 负责对整个集群的资源管理,但是在任务资源的调度方面只负责将资源封装成 Container 分配给 ApplicationMaster 的一级调度,二级调度的细节将交给 ApplicationMaster 去完成,这大大减轻了 ResourceManager 的压力。
NodeManager 负责对单个节点的资源管理,并将资源信息、Container 运行状态、健康状况等信息上报给 ResourceManager。ResourceManager 为了保证 Container 的利用率,会监控 Container,如果 Container 未在有限的时间内使用,ResourceManager 将命令 NodeManager “杀死”Container,以便将资源分配给其他任务。
Spark
前面说了这么多 hadoop,终于到 Spark 了。
Spark 特点
Spark 看到了 MRv1的问题,对 MapReduce 做了大量优化,总结如下:
1. 减少磁盘 I/O: MapReduce 会将计算中间结果存储到 HDFS 上,后续计算再从 HDFS 上读取数据计算,这样势必造成磁盘 I/O 成为瓶颈。Spark 将内容存储在内存中,减少了磁盘I/O,但是确增加了对内存的大量需求。
2. 增加并行度: Spark 任务划分为不同的 stage,允许多个 stage 即可以串行执行,又可以并行执行。
3. 避免重新计算: 当 stage 中某个分区的 Task 执行失败后,会重新对此 stage调度,但在重新杜调度的时候回过滤已经执行成功的分区任务,避免重复计算和资源浪费。
4. 可选的 Shuffle 排序: Hadoop MapReduce 在 Shuffle 之前有着固定的排序操作,而 Spark 则可以根据不同场景选择在 map 端排序还是 reduce 端排序。
5. 灵活的内存管理策略: Spark 将内存分为堆上的存储内存、堆外的存储内存、堆上的执行内存、堆外的执行内存4个部分。执行内存与存储内存并不是有明显的边界,而是"软"边界,执行内存和存储内存的任意一方在资源不足时都可以借用另一方的内存。
6. 其他特点: 检查点支持、易于使用、支持交互、支持 SQL、支持流式计算、可用性高、丰富的数据源支持和丰富的文件格式支持。
Spark 基础概念
1. RDD(resillient distributed dataset): 弹性分布式数据集。通过 Spark 的 转换 API 可以将 RDD 封装成一系列具有血缘关系的 RDD,也就是 DAG。只有通过 Spark 的 动作 API 才会将 RDD 及其 DAG 提交到 DAGScheduler。RDD 的祖先一定是一个跟数据源相关的 RDD,负责从数据源迭代读取数据。
2. DAG(Directed Acycle Graph): 有向无环图。Spark 使用 DAG 来反映各 RDD 之间的依赖或血缘关系。
3. Partition: 数据分区,即一个 RDD 的数据可以划分为多少个分区。Spark 根据 Partition 的数量来确定 Task 的数量。
4. NarrowDependency: 窄依赖,即子 RDD 依赖于父 RDD 中固定的 Partition。NarrowDependency分为 OneToOneDependency 和 RangeDependency 两种。
5. ShuffleDependency: Shuffle 依赖,也称为宽依赖,即子 RDD 对父 RDD 中的所有 Patition 都可能产生依赖。子 RDD 对父 RDD 各个 Partition 的依赖将取决于分区计算器(Partitioner)的算法。
6. Job: 用户提交的作业。当 RDD 及其 DAG 被提交给 DAGScheduler 调度后,DAGScheduler 会将所有 RDD 中的转换及动作视为一个 Job。一个 Job 有一个到多个 Task 组成。
7. Stage: Job 的执行阶段。DAGScheduler 按照ShuffleDependency 作为 Stage 的划分节点对 RDD的 DAG 进行 Stage 划分(上游的 Stage 将为 ShuffleMapStage)。因此一个 Job 可能被划分为一到多个 Stage。Stage 分为 ShuffleMapStage 和 ResultStage 两种。
8. Task: 具体执行任务。一个 Job 在每个 Stage 内都会按照 RDD 的 Partition 数量,创建多个 Task。Task 分为 ShuffleMapTask 和 ResultTask 两种。ShuffleMapStage中的 Task 为 ShuffleMapTask,而 ResultStage 中的 Task 为 ResultTask。ShuffleMapTask 和 ReduceTask 类似于 Hadoop 中的 Map 任务和 Reduce 任务。
9. Shuffle: Shuffle 是所有MapReduce 计算框架的核心执行阶段,Shuffle 用于打通 Map 任务(在 Spark 中就是 ShuffleMapTask)的输出与 reduce 任务(在 Spark 中就是 ResultTask)的输入,map 任务的中间结果按照指定的分区策略(例如:按照 key 哈希)分配给处理某个分区的 reduce 任务。
这么多概念,搞不懂,没图你说个 JB 啊,大家结合一下