spark shuffle有四种方式,分别是

  1. hashshuffle
  2. 优化后的hashshuffle
  3. sortshuffle
  4. bypass

一、hashshuffle与优化

一开始spark的shuffle方式是hashshuffle。hashshuffle有一个严重的问题,就是产生的小文件数量比较多。

我们知道,shuffle分为map端的shuffle write 和reduce端的shuffle read。

hashshuffle,每个task为下游每一个task都创建了一个文件,所以就产生了M*R的小文件数量,其中M是map的task数量,R是reduce的数量。

为了减少小文件的数量,spark随后提出了优化后的hashshuffle,即每个core处理的task文件的结果合并起来

比如:task1 write了3个小文件,里面是keyA keyB keyC,分别叫BlockA BlockB BlockC 吧

这时又来了一个task2,优化前task2 也产生三个小文件BlockA BlockB BlockC。这样就有6个小文件

但是task2 和task1是同一个core处理的,所以task2的BlockA BlockB BlockC 写入了task1 的BlockA BlockB BlockC,由于同一个文件中是同一个下游task处理的,所以只要追加写入就可以了。

该优化方法,本质上是小文件的合并(同一个core的小文件数量合并),朝着这个方向,我们可以继续优化成“同一个executor(jvm)产生的同一个下游task的小文件合并”,和同一台机器合并,不过spark并没有实现这些优化。

由于同一个stage里的map的task不分先后顺序,但是同一个core里的task是先后处理的。所以同一个jvm或者同一台机器的小文件合并,没有同一个core的合并那么简单。

spark也提出过内存共享的优化方法,跨executor(jvm)去共享一台机器上面的内存。

该优化最终将小文件数量下降了X倍,X为平均每个core处理的task数量,

  • 比如,有100个map,下游是500个reduce,那原本是100*500=5w的小文件数量,现在map端分配的core是20个(每个core处理5个task),就变成了20*500=1w,缩小了5倍。

该优化方法虽然好,但是受限于机器并行的能力,如果机器并行的能力强,分配的core数量接近于map的数量,该优化就十分有限。

二、sortshuffle

为了实现更进一步的小文件合并,spark在随后提出了sortshuffle。

sortshuffle的目的,还是实现"更宽"的“共享”。

上述讲到hashshuffle对spark的小文件做了同一个core的合并,但是由于不同core的task没有先后顺序,很难合并。sortshuffle就是为了实现“每台机器上的map task小文件都合并”。

因为一台机器上的task之间执行没有顺序,所以要等待所有的task执行完成。无法有效利用并发能力。

如果按照之前的做法,为每个reduce task创建一个文件,并且同一台机器上的同一个reduce task的文件合并的话,这样最多能节省的倍数为平均一台机器处理的task数量,

  • 比如,有100个map,下游是500个reduce,那原本是100*500=5w的小文件数量,现在map端分配的core是20个,20个core在5台机器上(机器是4核的),就变成了5*500=2000,缩小了20倍。

显然优化者不满足于这种程度的优化,于是优化者这次对"*500" 动手了(reduce task的数量)。如何优化"*500", 原来一个map task 会产生500个task,现在将这些都合并了。所以就没有了"*500"

  • 比如,有100个map,下游是500个reduce,那原本是100*500=5w的小文件数量,现在合并了同一个map task产生的reduce task,那么文件数量就变成了100个。虽小了500倍,(由于要增加索引文件,所以实际上要除以2,是250倍)

只不过如果一个map,为500个reduce task 产生一个文件,这500个reduce task要怎么使用呢?答案是排序+索引,这就是sortshuffle的由来。

为了实现500个reduce task对一个文件的高效操作,map write的文件内部是有序的,并且还为该文件提供了一个索引文件。这样reduce task 就可以根据索引文件在该文件中找到自己对应的数据了。

该方式的map write的方式也发生了改变,类似于lsm的操作那样,每次内存写满了就一次性把内存数据进行排序,写入到磁盘生成一个文件,等到所有的数据都写入磁盘后,再对所有生成的文件进行一个归并排序。

这种shuffle的好处是大大减少了小文件的数量,且优化的力度不受限于reduce task的数量,只受限于map的数量。坏处是增加了一个排序的开销。

既然map write产生的文件都是有序的,为什么不再对同一台机器或core的的文件进行归并呢。答案是没有必要,太多的小文件会增大网络IO的开销,太少的文件对降低并发的利用率。不过,如果上游的数据量很大(map数量很多),而该key的基数(去重后的数量)比较小的话,还是可以考虑按core或jvm或机器级别进行合并的(合并还要考虑到索引的合并),不过spark没有提供这类优化。

三、bypass

bypass是sortshuffle的一种优化。上述提到,map write的文件内部做了全局排序。是为了多个reduce task能找到各自需要read的数据。但是在一个reduce task内,数据并不需要有序,而reduce task的数量一般远远小于数据行数(数据key的基数),所以这就造成了计算的浪费。

为了停止这种浪费,spark提出了新的shuffle优化bypass,即改变map write的方式,在write的时候,原来要对每一个flush到磁盘的小文件进行排序,现在不排序了,复制之前hashshuffle的做法,为每个reduce task写一个小文件。最后,将这些同一个map产生的小文件合并成一个大文件,合并的方式很简单,就直接追加就可以了,最后对结果文件增加一个索引,这样下游每一个reduce task都能找到自己要读的数据。这样既省下了排序的开销,又将小文件数量缩小到了2*M的数量(和sortshuffle一样)。可谓是兼具了hashshuffle和sortshuffle的优点。

不过该方法也有限制的地方。该shuffle不支持预聚合,map的数量也尽量要小(和最初的hashshuffle一样,map数量过大会产生过多的临时文件)

触发bypass的map数量上限可以用参数 spark.shuffle.sort.bypassMergeThreshold 设置

spark_shuffle方式的演进过程的更多相关文章

  1. P9架构师讲解从单机至亿级流量大型网站系统架构的演进过程

    阶段一.单机构建网站 网站的初期,我们经常会在单机上跑我们所有的程序和软件.此时我们使用一个容器,如tomcat.jetty.jboos,然后直接使用JSP/servlet技术,或者使用一些开源的框架 ...

  2. 阿里P9架构师讲解从单机至亿级流量大型网站系统架构的演进过程

    阶段一.单机构建网站 网站的初期,我们经常会在单机上跑我们所有的程序和软件.此时我们使用一个容器,如tomcat.jetty.jboos,然后直接使用JSP/servlet技术,或者使用一些开源的框架 ...

  3. Spring-cloud微服务实战【一】:微服务的概念与演进过程

    本文是一个系列文章,主要讲述使用spring-cloud进行微服务开发的实战.在开始之前,我们先说一下从传统的单一部署架构到微服务的发展过程,以便让童鞋们更好的理解微服务的概念与演进过程. 1.单体架 ...

  4. RPC 是通信协议吗 ?→ 我们来看下它的演进过程

    开心一刻 一实习小护士给我挂针,拿着针在我胳膊上扎了好几针也没找到血管 但这位小姑娘真镇定啊,表情严肃认真,势有不扎到血管不罢休的意思 十几针之后,我忍着剧痛,带着敬畏的表情问小护士:你这针法跟容嬷嬷 ...

  5. 框架源码系列四:手写Spring-配置(为什么要提供配置的方法、选择什么样的配置方式、配置方式的工作过程是怎样的、分步骤一个一个的去分析和设计)

    一.为什么要提供配置的方法 经过前面的手写Spring IOC.手写Spring DI.手写Spring AOP,我们知道要创建一个bean对象,需要用户先定义好bean,然后注册到bean工厂才能创 ...

  6. 图解 Kafka 超高并发网络架构演进过程

    阅读本文大约需要 30 分钟. 大家好,我是 华仔, 又跟大家见面了. 上一篇作为专题系列的第一篇,我们深度剖析了关于 Kafka 存储架构设计的实现细节,今天开启第二篇,我们来深度剖析下「Kafka ...

  7. Java的类演进过程

    1.从面向过程到面向对象 在大家最熟悉的C语言中,如果要定义一个复杂的数据类型就用结构体(Struct)来实现,而为结构体的每个操作都定义一个函数,这个函数与结构体本身的定义没有任何关系.程序的重心集 ...

  8. WINDOWS硬件通知应用程序的常方法(五种方式:异步过程调用APC,事件方式VxD,消息方式,异步I/O方式,事件方式WDM)

    摘要:在目前流行的Windows操作系统中,设备驱动程序是操纵硬件的最底层软件接口.为了共享在设备驱动程序设计过程中的经验,给出设备驱动程序通知应用程序的5种方法,详细说明每种方法的原理和实现过程,并 ...

  9. 浏览器打开URL的方式和加载过程

    不同浏览器的工作方式不完全一样,大体上,浏览器的核心是浏览器引擎,目前市场占有率最高的几种浏览器几乎都使用了不同的浏览器引擎:IE使用的是Trident.Firefox使用的是Gecko.Safari ...

随机推荐

  1. 解决Spring中使用Example无法查询到Mongodb中的数据问题

    1 问题描述 在Spring Boot中使用Mongodb中的Example查询数据时查询不到,示例代码如下: ExampleMatcher matcher = ExampleMatcher.matc ...

  2. Go-23-接口

    接口定义 type 接口名 interface{ 方法1(参数列表) [返回值] 方法2(参数列表)[返回值] } 接口实现 func (变量 结构体类型)方法1 ([参数列表])(返回值){ } f ...

  3. WinForm只运行运行一个实例(单开)

    using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.F ...

  4. 1.7.6- 浏览器审查HTML标签元素

    或者F12

  5. 1.7.1- HTML表格table

    存在即是合理的,表格的是一种常用的标签,不是用来布局,常见是用处理 适合用表格table的地方: 创建表格:

  6. Mysql 事务特性和隔离级别

    事务的特性 原子性(Atomicity) 一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节.事务在执行过程中发生错误,会被回滚(Rollback)到 ...

  7. IPC$共享和其他共享(C$、D$)

    目录 net use共享命令的用法 IPC$ IPC空连接 ipc$使用的端口 关闭IPC$共享 net use共享命令的用法 net use #查看连接 net share              ...

  8. 8张图带你了解iptables的前世今生

    1 安全技术和防火墙 1 安全技术和防火墙 入侵检测系统(Intrusion Detection Systems):特点是不阻断任何网络访问,量化.定位来自内 外网络的威胁情况,主要以提供报警和事后监 ...

  9. 序列化-Json

    常见的Json转换工具有google的gson和阿里的fastgson.目前主流策略:POJO序列化成JSON字符串用Gson库,JSON字符串反序列化为POJO,用fastJson库. Netty中 ...

  10. java 多线程 实现多个线程的顺序执行

    场景 编写一个程序,启动三个线程,三个线程的name分别是A,B,C:,每个线程将自己的ID值在屏幕上打印5遍,打印顺序是ABCABC... 使用 synchronized 实现 public cla ...