spark和mapreduce的区别
- mapreduce是多进程模型,即里面的一个task对应一个进程
- 优点:进程便于更细粒度的控制每个任务的占用资源,但是启动慢
- spark是多线程模型,任务以分区为单位,一个分区对应一个task
- 任务以线程的方式运行,启动快
-
模型对比
-
mapreduce
- 主节点resourcemanager负责分配资源调度
- 从节点nodemanager负责计算,appmaster和container都是运行从节点nodemanager上
- 一个job应用对应一个appmaster,一个appmaster管理多个container,
- 一个container内部单线成执行(分配的是多核心cpu也是单线程)一个maptask或一个reducetask
- 如果运行是resourcemanager挂掉或主备切换,正在运行的任务会等待resourcemanager重启或切换好后再继续运行,并任务可能失败
-
spark
- 资源调度器clustermanager
- standAlone版主节点master负责分配资源调度
- 接受任务命令Worker启动Driver和Executor
- yarn模式 ResourceManager负责
- standAlone版主节点master负责分配资源调度
- 从节点worker负责任务计算 如果是Yarn模式下就是NodeManager节点
- 一个任务Job对应一个driver,一个driver可以有多个Executor,一个Executor可以有多个线程
- 资源分配后driver和Executors通讯,不需要和master进行通讯的,因此主节点挂机不影响正在运行的人去
- 资源调度器clustermanager
-
1.spark的并行度:
并行度指一个stage中可以同时并发执行task的能力
-
分区数量
- 默认和block相同,也可以手动设定例如sc.text.File("words.txt",10)设定10个分区,
- 注意设定10个分区和并行任务数量并不等价,如果设置并行度为20,那么对多运行10,剩下的10个不运行,但是还占用资源
- 参数spark.files.maxPartitionBytes 表示一个分片最大的大小默认是和block大小相同,目前没有后找到该参数的具体用法和调优案例,后期补充
- 文件源码底层与Hadoop有关,遵循Hadoop文件切片规则
-
并行度
- 并行数量=Executor数目*每个Executor核数
- 注意:如果一个spark任务有100个分区,对应100个任务,而并行能力15,那么要运行7次才能运行完任务,并且却后一次只有个10task运行,5个空闲,但不会释放至于资源
-
设置Executor数目
-
standlone模式下
- 数目计算公式 execuoterNum = spark.cores.max/spark.executor.cores
- spark.cores.max表示指定spark程序需要的总核数
- spark.executor.cores 是指每个executor需要的核数
- 例如:--driver-memory 6g --total-executor-cores 288 --executor-cores 2 表示指定spark程序需要的总核数288,,每一个executor运行2个核,execuoterNum=288/2=144
-
在yarn模式下直接指定 –num-executors 这个参数指定
-
-
设置并行的task数量(很重要)
- 设置参数 spark.default.parallelism
- 参数说明:参数用于设置每个stage的默认task数量,
- 官方建议设置数量 num-executors * executor-cores的2~3倍较为合适,可以充分利用资源
- 如果没有设置spark.default.parallelism,那么默认并行度与block数量相同,并且假如设置并行度10,而综总核心数100,那么运行是只有10核工作,其他都空闲
- 设置参数 spark.default.parallelism
2.mapreduce并行度
mapreduce所属于对进程单线程模式,即一个task对应一个进程,一个进程中用一个线程来执行
-
maptask
- maptask对应的也是切片数,切片数量是要计算出来,不能手动直接指定;一个切片开启一个进程,切片调用FileInputFormat.getsplit()方法(一个container运行一个maptask/reducetask)
- 简单地按照文件的内容长度进行切片
- 切片大小默认block大小
- splicts大小=max(文件总大小/numSplits,min(minSize,blocksize))
- 切片不考虑整体,只考虑一个block,例如130M文件有两个block,切片大小设定120M,会有三个切片,120M,8M,10M
- 如果切片大于block,那么实际一个切片数量也是128M,不会将多个小文件合并对应参数
- mapreduce.input.fileinputformat.split.minsize 默认1
- mapreduce.input.fileinputformat.split.maxsize 默认 Long.MAXValue
- maptask对应的也是切片数,切片数量是要计算出来,不能手动直接指定;一个切片开启一个进程,切片调用FileInputFormat.getsplit()方法(一个container运行一个maptask/reducetask)
-
reducetask
- 多少个分区对应多少个reduce指定手动指定,job.setNumReduceTasks(4);默认是1,不使用分区(有设定分区也不会有效)
- 如果reducetask数a>分区数b,只有b个线程处理数据,其他不工作,如果a<b程序无法运行
-
优化相关 JVM重用
- mapred.job.reuse.jvm.num.tasks
- 默认情况下一个jvm进程运行一个maptask/reducetask。运行完后释放资源,进程消失,但是可以通过该参数来设置多个task,task必须是同一个job的,且执行是顺序执行
- mapred.job.reuse.jvm.num.tasks
切片调用spark中是重写了FileInputFormat.getsplit()方法,但是基本逻辑相同,多了一个手动指定的数量
public InputSplit[] getSplits(JobConf job, int numSplits) throws IOException {
Stopwatch sw = (new Stopwatch()).start();
FileStatus[] files = this.listStatus(job);
job.setLong("mapreduce.input.fileinputformat.numinputfiles", (long)files.length);
long totalSize = 0L;
FileStatus[] arr$ = files;
int len$ = files.length;
for(int i$ = 0; i$ < len$; ++i$) {
FileStatus file = arr$[i$];
if (file.isDirectory()) {
throw new IOException("Not a file: " + file.getPath());
}
totalSize += file.getLen();
}
//指定最小分区数可以划分的size大小
long goalSize = totalSize / (long)(numSplits == 0 ? 1 : numSplits);
//图片通过与hadoop的中最小值比较,获得比较大的
long minSize = Math.max(job.getLong("mapreduce.input.fileinputformat.split.minsize", 1L), this.minSplitSize);
ArrayList<FileSplit> splits = new ArrayList(numSplits);
NetworkTopology clusterMap = new NetworkTopology();
FileStatus[] arr$ = files;
int len$ = files.length;
for(int i$ = 0; i$ < len$; ++i$) {
FileStatus file = arr$[i$];
Path path = file.getPath();
long length = file.getLen();
if (length == 0L) {
splits.add(this.makeSplit(path, 0L, length, new String[0]));
} else {
FileSystem fs = path.getFileSystem(job);
BlockLocation[] blkLocations;
if (file instanceof LocatedFileStatus) {
blkLocations = ((LocatedFileStatus)file).getBlockLocations();
} else {
blkLocations = fs.getFileBlockLocations(file, 0L, length);
}
if (!this.isSplitable(fs, path)) {
String[][] splitHosts = this.getSplitHostsAndCachedHosts(blkLocations, 0L, length, clusterMap);
splits.add(this.makeSplit(path, 0L, length, splitHosts[0], splitHosts[1]));
} else {
//获取文件的快大小,如果是本地文件是33554432k对应32兆
long blockSize = file.getBlockSize();
//三个大小比较,得到最后切片大小
long splitSize = this.computeSplitSize(goalSize, minSize, blockSize);
long bytesRemaining;
String[][] splitHosts;
for(bytesRemaining = length; (double)bytesRemaining / (double)splitSize > 1.1D; bytesRemaining -= splitSize) {
splitHosts = this.getSplitHostsAndCachedHosts(blkLocations, length - bytesRemaining, splitSize, clusterMap);
splits.add(this.makeSplit(path, length - bytesRemaining, splitSize, splitHosts[0], splitHosts[1]));
}
if (bytesRemaining != 0L) {
splitHosts = this.getSplitHostsAndCachedHosts(blkLocations, length - bytesRemaining, bytesRemaining, clusterMap);
splits.add(this.makeSplit(path, length - bytesRemaining, bytesRemaining, splitHosts[0], splitHosts[1]));
}
}
}
}
//三个大小的比较算法
protected long computeSplitSize(long goalSize, long minSize, long blockSize) {
return Math.max(minSize, Math.min(goalSize, blockSize));
}