Spark的Shuffle过程

Spark的Shuffle过程

说明

  • shuffle操作,是在spark操作中调用了一些特殊算子才会触发的一种操作,

  • shuffle操作,会导致大量的数据在不同的节点之间传输,

  • 由上可知,shuffle过程是spark中最复杂、最消耗性能的一种操作

举例

  • reduceByKey算子会将一个RDD中的每一个key对应的所有value都聚合成一个value,然后生成一个新的RDD,新的RDD的元素就是的格式,每一个key对应一个聚合起来的value,在这里,最大的问题在于,对于上一个RDD来说,并不是一个key对应所有的value都在一个partition中的,更不是不太可能key的所有value都在一个节点上,对于这种情况,就必须在集群中将各个节点上同一个key对应的values统一传输到一个节点上进行聚合处理,这个过程势必发生大量的网路IO。

shuffle的几个过程(会在不同的stage中进行)

  • stuffle write过程

    • 在进行一个key对应的values的聚合时,首先,上一个stage的每一个map task就必须保证将自己处理的当前分区中的数据相同key写入一个分区文件中,可能会多个不同的分区文件
  • shuffle read过程

    • 接着下一个stage的reduce task就必须从上一个stage的所有task所在的节点上,将各个task写入的多个分区文件中找到属于自己的分区文件,然后将属于自己的分区数据拉过来,这样就可以保证每一个key对应所有values都汇聚到一个节点行进行处理和聚合,这个过程就称之为shuffle

shuffle过程中的分区排序问题

  • 默认情况下,shuffle操作是不会对每个分区中的数据进行排序。

  • 如果想要对每个分区数据行进排序,可以使用三种方法:

    • 1、使用mapPartitions算子把每个partition取出来进行排序
    • 2、使用repartitionAndSortWithinPartition(该算子是对RDD进行重分区的算子),在重新分区的过程中同时进行分区内数据的排序
    • 3、使用sortByKey对所有分区的数据进行全局排序
  • 以上三种方法,mapPartitions代价比较小,因为不需要进行额外的shuffle操作,repartitionAndSortWithinPartitions和sortByKey可能进行额外的shuffle操作,因此性能并不是很高。

会导致shuffle的算子

  • byKey类的算子:比如reduceByKey、groupByKey、sortByKey、aggregateByKey、combineByKey

  • repartition类的算子:比如repartition(少量分区变成多个分区会发生shuffle)、repartitionAndSortWithinPartitions、coalesce(需要指定是否发生shuffle)、partitionBy

  • join类的算子:比如join(先groupByKey后再join就不会发生shuffle)、cogroup

  • 注意:首先对于上述操作,能不用shuffle操作,就尽量不用,尽量使用不发生shuffle的操作。 其次,如果使用了shuffle操作,那么肯定要进行shuffle的调优,甚至是解决遇到的数据倾斜问题。

总结:

  • shuffle操作是spark中唯一最消耗性能的过程,因此也就成了最需要进行性能调优的地方,最需要解决线上报错的地方,也就是唯一可能出现数据倾斜的地方

  • 为了实时shuffle操作,spark才有stage的概念,在发生shuffle操作的算子中,需要进行stage的划分shuffle操作的前半部分,属于上一个stage的范围,通常称之为map task,shuffle操作的后半部分,属于下一个stage的范围,通常称之为reduce task,其中map task负责数据的组织,也就是将同一个key对应的value都写入同一个下游task对应的分区文件中,其中reduce task负责数据的聚合,也就是将上一个stage的task所在的节点上,将属于自己的各个分区文件都拉取过来进行聚合。

  • map task会将数据先保存在内存中,如果内存不够时,就溢写到磁盘文件中,reduce task会读取各个节点上属于自己的分区磁盘文件到自己节点的内存中进行聚合。

  • 由此可见,shuffle操作会消耗大量的内存,因为无论是网络传输数据之前还是之后,都会使用大量内存中数据结构来实施聚合操作,在聚合过程中,如果内存不够,只能溢写到磁盘文件中去,此时就会发生大量的网络IO,降低性能。

  • 此外,shuffle过程中,会产生大量的中间文件,也就是map side写入的大量分区文件,这些文件会一直保留着,直到RDD不再被使用,而且被gc回收掉了,才会去清理中间文件,这主要是为了:如果要重新计算shuffle后RDD,那么map side不需要重新再做一次磁盘写操作,但是这种情况下,如果在应用程序中一直保留着对RDD的引用,导致很长的时间以后才会进行回收操作,保存中间文件的目录,由spark.local.dir属性指定。

  • 所以,spark性能的消耗体现在:内存的消耗、磁盘IO、网络的IO

你可能感兴趣的:(大数据,Spark)