MapReduce和Spark
MapReduce
简介
MapReduce是一个面向离线批处理的分布式计算框架。
离线:对时间不敏感,慢慢算
批处理:数据攒一批,处理一批(相对于流处理)
分布式编程模型:MapReduce程序被分为Map(映射)阶段和Reduce(化简)阶段
特点:
- 计算跟着数据走
- 良好的扩展性:计算能力随着节点数增加,近似线性增长
- 高容错
- 状态监控
- 适合海量数据的离线批处理
- 降低了分布式编程的门槛
使用场景:对时间不敏感,非流式数据(不适用OLAP、流计算、DAG计算)
原理示例
WordCount示例
实际上shuffle是虚拟的,一边做一半。
基本概念
作业Job与任务Task
作业是客户端请求执行的一个工作单元(包括输入数据、MapReduce程序、配置信息);任务是将作业分解后得到的细分工作单元(分为Map任务和Reduce任务)
Split(切片)
输入数据被划分成等长的小数据块,称为输入切片(Input Split),简称切片。spilt是一个逻辑概念,仅包含元数据信息,如数据的起始位置、长度、所在节点等。有多少个切片就会有几个任务启动,切片大小默认等于HDFS的block大小(因为MapReduce取数最大的情况下是从block里面取),它的划分方式由程序设定,与Block无严格对应关系(切片是个逻辑概念)。
Split越小,Map任务越多,并发度越高,但开销也越大;Split越大,任务越少,并发度降低。
Map阶段(映射)
由若干Map任务组成,任务数量由Split数量决定。
输入:Split切片(key-value)
输出:中间计算结果(key-value)
Reduce阶段(化简)
由若干Reduce任务组成,任务数量由程序指定
输入:Map阶段输出的中间结果(key-value)
输出:最终结果(key-value)
Shuffle阶段(混洗)
Shuffle是Map和Reduce之间的强依赖关系(Shuffle依赖)导致的,即每个Reduce的输入依赖于所有Map的输出
Map和Reduce阶段的中间环节(虚拟阶段),分为Map端Shuffle和Reduce端Shuffle。
包括Partition(分区)、Sort(排序)、Spill(溢写)、Merge(合并)和Fetch(抓取)等工作。
如上图,Reduce任务数量决定了Partition数量,Partition编号 = Reduce任务编号。
分区Partition:
利用“哈希取模”对Map输出数据分区,即Partition编号 = key hashcode % reduce task num(%为取模)。
由于Shuffle阶段很耗资源,避免和减少Shuffle是MapReduce程序调优的关键。
作业运行模式
在MapReduce2.x中的YARN模式下,运行模式如下:
Spark
简介
Spark解决了MapReduce的局限性,它的出现改变了大数据的生态。
MapReduce缺陷在于:语义较少(仅支持Map、Reduce两种语义操作),模型较为粗糙(划分为两个阶段);无法避免Shuffle所以效率较低;不适合迭代计算、在线分析、实时流处理等场景。另外,计算框架多、选型难导致学习成本很高。
概念
高性能的分布式通用计算引擎,包括:
- Spark Core:核心计算框架
- Spark SQL:结构化数据查询
- Spark Streaming:实时流处理
- Spark MLib:机器学习
- Spark GraphX:图计算
Spark具有有高吞吐、低延时、通用易扩展、高容错等特点,采用Scala语言开发,提供多种运行模式(可本地可单机可集群)。
特点:
- 计算高效:语义操作多样,模型设计精细;利用RDD内存计算和Cache缓存机制,支持迭代计算和数据共享,减少数据读取的IO开销;利用DAG引擎,减少中间计算结果写入HDFS的开销;利用多线程池模型,减少任务启动开销。
- 通用易用:适用于批处理、流处理、在线分析、机器学习等场景;提供了丰富的开发API,支持Scala、Java、Python、R等。
- 运行模式多样:Local模式、Standalone模式、YARN/Mesos模式。
相比MapReduce效率提升在一个数量级以上。
编程模型
RDD
数据模型:弹性分布式数据集(Resilient Distributed Datesets,RDD)。Spark就是通过基于RDD进行计算。
RDD相当于一张表,由分布在集群中的多个Partition组成。
Partition分布在集群的不同节点中,主要在内存中,只读。如果数据出现错误或问题了会自动重构(体现了弹性)。
和MapReduce的split相比,区别主要在弹性分布和驻留内存。
RDD操作(Operator)
- Transformation(转换)
将Scala集合或Hadoop输入数据构造成一个新RDD(从无到有);通过已有的RDD产生新RDD(从有到新)。
Transformation特点是惰性执行,只记录转换关系,不触发计算。例如:map、filter、flatmap、union、distinct、sortbykey都是Transformation操作。 - Action操作
通过RDD计算得到结果或者落盘;真正触发计算,例如:first、count、collect、foreach、saveAsTextFile都是Action操作。
RDD依赖(Dependency)
分为窄依赖和宽依赖。
窄依赖(Narrow Dependency):
每个父RDD分区只能为一个子RDD分区供数,子分区所依赖的父分区集合之间没有交集;子RDD分区数据丢失或损坏,从其依赖的父RDD分区重新计算即可,无需Shuffle。例如:map、filter、union都是窄依赖的关系。
宽依赖(Wide/Shuffle Dependency):
每个父RDD分区为所有子RDD分区供数;子RDD区数据丢失或损坏,从所有父RDD分区重新计算,必须Shuffle;相对于窄依赖,宽依赖付出的代价要高很多,尽量避免使用(资源消耗太高)。例如:groupByKey、reduceByKey、sortByKey都是宽依赖。
作业运行模式
抽象模式
Master-Slave架构。把Driver注册到ApplicationMaster中(大数据各种框架是热插拔),告诉YARN是什么。
Driver:
- 每个Spark作业启动一个Driver(作业管家),每个Driver创建一个SparkContext(负责加载配置信息,初始化运行环境,创建DAGScheduler和TaskScheduler)
DAGScheduler:根据任务的依赖关系建立DAG(Directed Acyclic Graph, 有向无环图),根据依赖关系是否为宽依赖,即是否存在Shuffle,将DAG划分为不同的阶段(Stage),最终将各阶段Task组成的TaskSet提交给TaskScheduler。
TaskScheduler:负责任务调度;重新提交失败的Task;为执行速度慢的Task启动备用Task。其中的Executor负责执行Driver分发的任务,一个节点可以启动多个Executor,每个Executor通过多线程运行多个任务(真正在后台跑的是Executor)。Task是Spark运行的基本单位,负责处理若干RDD分区的计算逻辑,在DAG阶段划分为窄依赖的可以一起执行。 - 负责Spark作业解析(生成逻辑和物理计划,以及DAG)、任务调度
作业运行模式主要分为三种:
- Local模式
单机运行,通常用于测试 - Standalone模式
Spark集群可以独立运行,不依赖于第三方资源管理系统,如YARN、Mesos。采用Master/Slave架构:Master统一管理集群资源,Worker负责本地计算,Driver一对一管理作业;ZooKeeper负责Master HA,避免单点故障。
适用于集群规模和数据量都不大的情况。 - YARN模式
YARN-Client模式:适用于交互和调试;YARN-Cluster模式:适用于生产环境。
课后温习
简述MR Split与HDFS Block的关系。
为什么MapReduce要求输入输出必须是key-value键值对?
简述Shuffle的工作原理。
从编程模型的视角,MapReduce有哪些优缺点?
简述“哈希取模”在MapReduce中的作用。
参考
课程内容来自:南京大学+星环科技课程,大数据大数据理论与实践课程