Apache Spark探秘:Spark Shuffle实现html
三月 26, 2014 at 9:40 上午 by Donggit
Tags: Spark
github
做者:Dong | 新浪微博:西成懂 | 能够转载, 但必须以超连接形式标明文章原始出处和做者信息及版权声明
网址:http://dongxicheng.org/framework-on-yarn/apache-spark-shuffle-details/
本博客的文章集合:http://dongxicheng.org/recommend/apache
本博客微信公共帐号:hadoop123(微信号为:hadoop-123),分享hadoop技术内幕,hadoop最新技术进展,发布hadoop相关职位和求职信息,hadoop技术交流聚会、讲座以及会议等。二维码以下:微信
对于大数据计算框架而言,Shuffle阶段的设计优劣是决定性能好坏的关键因素之一。本文将介绍目前Spark的shuffle实现,并将之与MapReduce进行简单对比。本文的介绍顺序是:shuffle基本概念,MapReduce Shuffle发展史以及Spark Shuffle发展史。架构
(1) shuffle基本概念与常见实现方式并发
shuffle,是一个算子,表达的是多对多的依赖关系,在类MapReduce计算框架中,是链接Map阶段和Reduce阶段的纽带,即每一个Reduce Task从每一个Map Task产生数的据中读取一片数据,极限状况下可能触发M*R个数据拷贝通道(M是Map Task数目,R是Reduce Task数目)。一般shuffle分为两部分:Map阶段的数据准备和Reduce阶段的数据拷贝。首先,Map阶段需根据Reduce阶段的Task数量决定每一个Map Task输出的数据分片数目,有多种方式存放这些数据分片:框架
1) 保存在内存中或者磁盘上(Spark和MapReduce都存放在磁盘上);oop
2) 每一个分片一个文件(如今Spark采用的方式,若干年前MapReduce采用的方式),或者全部分片放到一个数据文件中,外加一个索引文件记录每一个分片在数据文件中的偏移量(如今MapReduce采用的方式)。源码分析
在Map端,不一样的数据存放方式各有优缺点和适用场景。通常而言,shuffle在Map端的数据要存储到磁盘上,以防止容错触发重算带来的庞大开销(若是保存到Reduce端内存中,一旦Reduce Task挂掉了,全部Map Task须要重算)。但数据在磁盘上存放方式有多种可选方案,在MapReduce前期设计中,采用了如今Spark的方案(目前一直在改进),每一个Map Task为每一个Reduce Task产生一个文件,该文件只保存特定Reduce Task需处理的数据,这样会产生M*R个文件,若是M和R很是庞大,好比均为1000,则会产生100w个文件,产生和读取这些文件会产生大量的随机IO,效率很是低下。解决这个问题的一种直观方法是减小文件数目,经常使用的方法有:
1) 将一个节点上全部Map产生的文件合并成一个大文件(MapReduce如今采用的方案),
2) 每一个节点产生{(slot数目)*R}个文件(Spark优化后的方案)。对后面这种方案简单解释一下:不论是MapReduce 1.0仍是Spark,每一个节点的资源会被抽象成若干个slot,因为一个Task占用一个slot,所以slot数目可当作是最多同时运行的Task数目。若是一个Job的Task数目很是多,限于slot数目有限,可能须要运行若干轮。这样,只须要由第一轮产生{(slot数目)*R}个文件,后续几轮产生的数据追加到这些文件末尾便可。
所以,后一种方案可减小大做业产生的文件数目。
在Reduce端,各个Task会并发启动多个线程同时从多个Map Task端拉取数据。因为Reduce阶段的主要任务是对数据进行按组规约。
也就是说,须要将数据分红若干组,以便以组为单位进行处理。你们知道,分组的方式很是多,常见的有:Map/HashTable(key相同的,放到同一个value list中)和Sort(按key进行排序,key相同的一组,经排序后会挨在一块儿),这两种方式各有优缺点,第一种复杂度低,效率高,可是须要将数据所有放到内存中,第二种方案复杂度高,但可以借助磁盘(外部排序)处理庞大的数据集。Spark前期采用了第一种方案,而在最新的版本中加入了第二种方案, MapReduce则从一开始就选用了基于sort的方案。
(2) MapReduce Shuffle发展史
【阶段1】:MapReduce Shuffle的发展也并非一马平川的,刚开始(0.10.0版本以前)采用了“每一个Map Task产生R个文件”的方案,前面提到,该方案会产生大量的随机读写IO,对于大数据处理而言,很是不利。
【阶段2】:为了不Map Task产生大量文件,HADOOP-331尝试对该方案进行优化,优化方法:为每一个Map Task提供一个环形buffer,一旦buffer满了后,则将内存数据spill到磁盘上(外加一个索引文件,保存每一个partition的偏移量),最终合并产生的这些spill文件,同时建立一个索引文件,保存每一个partition的偏移量。
(阶段2):这个阶段并无对shuffle架构作调成,只是对shuffle的环形buffer进行了优化。在Hadoop 2.0版本以前,对MapReduce做业进行参数调优时,Map阶段的buffer调优很是复杂的,涉及到多个参数,这是因为buffer被切分红两部分使用:一部分保存索引(好比parition、key和value偏移量和长度),一部分保存实际的数据,这两段buffer均会影响spill文件数目,所以,须要根据数据特色对多个参数进行调优,很是繁琐。而MAPREDUCE-64则解决了该问题,该方案让索引和数据共享一个环形缓冲区,再也不将其分红两部分独立使用,这样只需设置一个参数控制spill频率。
【阶段3(进行中)】:目前shuffle被当作一个子阶段被嵌到Reduce阶段中的。因为MapReduce模型中,Map Task和Reduce Task能够同时运行,所以一个做业前期启动的Reduce Task将一直处于shuffle阶段,直到全部Map Task运行完成,而在这个过程当中,Reduce Task占用着资源,但这部分资源利用率很是低,基本上只使用了IO资源。为了提升资源利用率,一种很是好的方法是将shuffle从Reduce阶段中独立处理,变成一个独立的阶段/服务,由专门的shuffler service负责数据拷贝,目前百度已经实现了该功能(准备开源?),且收益明显,具体参考:MAPREDUCE-2354。
(3) Spark Shuffle发展史
目前看来,Spark Shuffle的发展史与MapReduce发展史很是相似。初期Spark在Map阶段采用了“每一个Map Task产生R个文件”的方法,在Reduce阶段采用了map分组方法,但随Spark变得流行,用户逐渐发现这种方案在处理大数据时存在严重瓶颈问题,所以尝试对Spark进行优化和改进,相关连接有:External Sorting for Aggregator and CoGroupedRDDs,“Optimizing Shuffle Performance in Spark”,“Consolidating Shuffle Files in Spark”,优化动机和思路与MapReduce很是相似。
Spark在前期设计中过多依赖于内存,使得一些运行在MapReduce之上的大做业难以直接运行在Spark之上(可能遇到OOM问题)。目前Spark在处理大数据集方面尚不完善,用户需根据做业特色选择性的将一部分做业迁移到Spark上,而不是总体迁移。随着Spark的完善,不少内部关键模块的设计思路将变得与MapReduce升级版Tez很是相似。
【其余参考资料】
原创文章,转载请注明: 转载自董的博客
本文连接地址: http://dongxicheng.org/framework-on-yarn/apache-spark-shuffle-details/
做者:Dong,做者介绍:http://dongxicheng.org/about/
本博客的文章集合:http://dongxicheng.org/recommend/