Spark Partition

partition是spark rdd计算的最小单元。为什么是最小单元?先从分布式说起,分布式计算的特点就是批处理,将大量的数据分成若干批次,使得利用廉价机器搭建的集群也可以完成海量数据的计算。大量的数据分散在集群中的若干节点上,每个节点上的那部分数据,在执行计算的时候,又可以切分成若干份,每一份就是一个批次,也就是一个partition。

spark计算的性能与partition的数量有很大的关系。每个task处理一个partition。如果partition的数量小于集群可用的CPU核数,就不能充分利用并行计算的性能,还可能引发数据倾斜的问题。但是partition的数量远大于集群可用CPU核数的时候,又可能导致资源分配的时间超过任务实际执行的时间。那么partition的数量又是由什么决定的呢?

备注: 这里提到的相关API及代码都是基于PySpark

一、数据源

rdd如果是从某些数据源创建的,则partition的数量与这些数据源有直接关系。下文的sc指SparkContext实例, spark指的是SparkSession实例。

从内存中创建

sc.parallelize(...)

这种方式创建的partition数量等于SparkContext的defaultParallelism的值,或者等于传给parallelize的numSlices参数。

读取HDFS文件创建

包括使用sc.textFile(...)spark.sql(...)查询Hive表得到的结果的partition数量等于HDFS中的文件块数。

二、通过计算

通过Generic Transformation创建

定义

rdd_size = rdd.getNumPartitions()
other_rdd_size = rdd.getNumPartitions()
  1. filter(), map(), flatMap(), distinct()partition数量等于parent RDD的数量。
  2. rdd.union(other_rdd)partition数量等于rdd_size + other_rdd_size
  3. rdd.intersection(other_rdd) partition数量等于max(rdd_size, other_rdd_size)
  4. rdd.subtract(other_rdd) partition数量等于rdd_size
  5. rdd.cartesian(other_rdd) partition数量等于rdd_size * other_rdd_size

通过Key-based Transformation创建

什么是key-based?key-based指的是执行transformation的rdd都必须是pair rdd,在pyspark中,并没有pair rdd这个类型,一个rdd要成为有效的pair rdd, 只需要rdd中的每条记录满足k, v = kv,也就是只要是一个支持迭代,且一共含有两个元素的对象即可,可以是(k, v) ,也可以是[k, v], 也可以是自定义类型等。

在默认的情况下:

  1. reduceByKey(), foldByKey(), combineByKey(), groupByKey() partition数量等于parent rdd的数量, 使用HashPartitioner,允许自己指定hash函数
  2. sortByKey() partition数量等于parent rdd的数量,使用RangePartitioner
  3. mapValues(), flatMapValues() partition数量等于parent rdd的数量,使用parent rdd的partitioner,这两个函数接收的parent rdd必须为pair rdd, 确保执行完以后保持原有partition不变
  4. join(), leftOuterJoin(), rightOuterJoin() partition数量取决于相关的两个rdd, 使用HashPartitioner

三、自定义partition数量

在使用HashPartitioner的transformation的函数中,可以通过numPartitions与partitionFunc改变默认的partition数量以及数据的分布方式。

numPartitions指定结果rdd的partition数量, partitionFunc只要是一个返回一个int数值的函数即可,它决定了一条记录将要被分配的partition的index

# pyspark 源码
buckets[partitionFunc(k) % numPartitions].append((k, v))

几个帮助了解partition的API

# 查看partition数量
rdd.getNumPartitions()

# 查看所用partitioner
rdd.partitioner 

# 查看具体数据在每个partition的分布
rdd.glom().collect()  # 仅适用小量数据, 数据量大慎用

在做试验的时候, 通过rdd.glom().collect()可以看到数据在每个partition的分布情况,有的时候可以看出, 当partition数量多于数据条数,或是数据倾斜的时候,有的partition是空的,所以spark不会因为某个partition是空的就移除它,而每个partition又对应一个task,为空的partition启停任务, 必然引起不必要的资源消耗而影响性能。 所以分配得当的partition数量是非常重要的。

你可能感兴趣的:(Data)