七、分区

7.1 基本概念

  1. 如果使用可控的分区方式,将经常被一起访问的数据放在同一个节点上,那么可以大大减少应用的通信开销。

    • 通过正确的分区,可以带来明显的性能提升
    • 为分布式数据集选择正确的分区,类似于为传统的数据集选择合适的数据结构
  2. 分区并不是对所有应用都是有好处的:如果给定的RDD 只需要被扫描一次,则我们完全没有必要对其预先进行分区处理。

    • 只有当数据集多次在诸如连接这种基于键的操作中使用时,分区才会有帮助。
  3. Spark 中所有的键值对RDD 都可以进行分区。系统会根据一个针对键的函数对元素进行分组。

    • spark 可以确保同一个组的键出现在同一个节点上
  4. 许多spark 操作会自动为结果RDD 设定分区

    • sortByKey() 会自动生成范围分区的RDD
    • groupByKey() 会自动生成哈希分区的RDD

    其它还有join()、leftOuterJoin()、rightOuterJoin()、cogroup()、groupWith()、groupByKey()、reduceByKey()、combineByKey()、partitionBy(),以及mapValues()(如果输入RDD有分区方式)、flatMapValues()(如果输入RDD有分区方式)

    对于map()操作,由于理论上它可能改变元素的键,因此其结果不会有固定的分区方式。

    对于二元操作,输出数据的分区方式取决于输入RDD的分区方式

    • 默认情况下,结果采用哈希分区
    • 若其中一个输入RDD 已经设置过分区方式,则结果就使用该分区方式
    • 如果两个输入RDD 都设置过分区方式,则使用第一个输入的分区方式
  5. 许多spark 操作会利用已有的分区信息,如join()、leftOuterJoin()、rightOuterJoin()、cogroup()、groupWith()、groupByKey()、reduceByKey()、combineByKey()、lookup() 。 这些操作都能从分区中获得收益。

    • 任何需要将数据根据键跨节点进行混洗的操作,都能够从分区中获得好处

7.2 查看分区

  1. .getNumPartitions 属性可以查看RDD 的分区数

7.3 指定分区

  1. 在执行聚合或者分组操作时,可以要求Spark 使用指定的分区数(即numPartitions 参数)

    • 如果未指定该参数,则spark 根据集群的大小会自动推断出一个有意义的默认值
  2. 如果我们希望在除了聚合/分组操作之外,也能改变RDD 的分区。那么Spark 提供了.repartition() 方法

    • 它会把数据通过忘了进行混洗,并创建出新的分区集合
    • 该方法是代价比较大的操作,你可以通过.coalesce() 方法将RDD的分区数减少。它是一个代价相对较小的操作。
  3. .repartition(numPartitions):返回一个拥有指定分区数量的新的RDD

    • 新的分区数量可能比旧分区数增大,也可能减小。
  4. .coalesce(numPartitions,shuffle=False):返回一个拥有指定分区数量的新的RDD

    • 新的分区数量必须比旧分区数减小
  5. .partitionBy(numPartitions, partitionFunc=<function portable_hash at 0x7f51f1ac0668>):返回一个使用指定分区器和分区数量的新的RDD

    • 新的分区数量可能比旧分区数增大,也可能减小。
    • 这里partitionFunc 是分区函数。注意:如果你想让多个RDD 使用同一个分区方式,则应该使用同一个分区函数对象(如全局函数),而不要给每个RDD 创建一个新的函数对象。
  6. 对于重新调整分区的操作结果,建议对其持久化。

    • 如果未持久化,那么每次用到这个RDD时,都会重复地对数据进行分区操作,性能太差