确定RDD分区
在Scala和java中,可以使用partitioner属性来决定怎么分区。scala.Option对象是Scala的容器类。你可以在Option上调用isDefined() 来检查是否有值,使用get()来获得值。如果存在,那么这个值是spark.Partitioner对象。这个实际上的功能是每个key存入哪个分区。
Example 4-24. Determining partitioner of an RDD
scala> val pairs = sc.parallelize(List((1, 1), (2, 2), (3, 3)))
pairs: spark.RDD[(Int, Int)] = ParallelCollectionRDD[0] at parallelize at <console>:12
scala> pairs.partitioner
res0: Option[spark.Partitioner] = None
scala> val partitioned = pairs.partitionBy(new spark.HashPartitioner(2))
partitioned: spark.RDD[(Int, Int)] = ShuffledRDD[1] at partitionBy at <console>:14
scala> partitioned.partitioner
res1: Option[spark.Partitioner] = Some(spark.HashPartitioner@5147788d)
上面的例子,我们创建了一个(int,int)的RDD,一开始没有分区信息(Option的值是None)。当我们首次用hash分区创建第2个RDD时。如果我们想要在将来的操作中使用分区,那么我们应该定义分区的地方,将persist()放在第三行的输入。这就是为什么之前的例子我们在userData上使用persist(),如果没有persist(),随后的RDD动作将重新评估整个分区,这将导致一次一次的hash分区。
操作受益于分区
影响分区的操作
Spark知道内部每个他们操作是怎么影响分区的,然后在RDDs上自动设置由分区数据操作创建的分区。例如,假设调用join()连接两个RDDs;因为相同的key的元素hashed到相同的机器,Spark知道hash分区的结果,类似于reduceByKey()加入到结果中会显著加快。
另一方面,对于不能产生已知分区的转换,输出的RDD将不会分区。例如,如果你在key/valueRDD的hash分区上调用map(),传递给map()函数理论上可以改变每个元素的key,所以结果不会分区。Spark不会分析你的函数会检查保留的key。相反的,它提供了两个操作,mapValues()和flatMapValues(),他保证每个元组的键保持不变。
所有这些,下面是输出RDD上设置分区的所有操作:cogroup(),groupWith(),join(),leftOuterJoin(),rightOuterJoin(),groupByKey(),reduceByKey(),combineByKey(),partitionBy(),sort(),mapValues(),flatMapValues()和filter()。所有的其他操作都不会产生分区。
最后,对于二进制操作,分区的设置依赖于父的RDD的分区。默认的是hash分区,分区的个数设置为并行操作的等级。然而,如果其中一个父RDD有分区,那么它就是那个分区,如果父RDD都有分区,他将是第一个父的分区。
在Scala和java中,可以使用partitioner属性来决定怎么分区。scala.Option对象是Scala的容器类。你可以在Option上调用isDefined() 来检查是否有值,使用get()来获得值。如果存在,那么这个值是spark.Partitioner对象。这个实际上的功能是每个key存入哪个分区。
Example 4-24. Determining partitioner of an RDD
scala> val pairs = sc.parallelize(List((1, 1), (2, 2), (3, 3)))
pairs: spark.RDD[(Int, Int)] = ParallelCollectionRDD[0] at parallelize at <console>:12
scala> pairs.partitioner
res0: Option[spark.Partitioner] = None
scala> val partitioned = pairs.partitionBy(new spark.HashPartitioner(2))
partitioned: spark.RDD[(Int, Int)] = ShuffledRDD[1] at partitionBy at <console>:14
scala> partitioned.partitioner
res1: Option[spark.Partitioner] = Some(spark.HashPartitioner@5147788d)
上面的例子,我们创建了一个(int,int)的RDD,一开始没有分区信息(Option的值是None)。当我们首次用hash分区创建第2个RDD时。如果我们想要在将来的操作中使用分区,那么我们应该定义分区的地方,将persist()放在第三行的输入。这就是为什么之前的例子我们在userData上使用persist(),如果没有persist(),随后的RDD动作将重新评估整个分区,这将导致一次一次的hash分区。
操作受益于分区
影响分区的操作
Spark知道内部每个他们操作是怎么影响分区的,然后在RDDs上自动设置由分区数据操作创建的分区。例如,假设调用join()连接两个RDDs;因为相同的key的元素hashed到相同的机器,Spark知道hash分区的结果,类似于reduceByKey()加入到结果中会显著加快。
另一方面,对于不能产生已知分区的转换,输出的RDD将不会分区。例如,如果你在key/valueRDD的hash分区上调用map(),传递给map()函数理论上可以改变每个元素的key,所以结果不会分区。Spark不会分析你的函数会检查保留的key。相反的,它提供了两个操作,mapValues()和flatMapValues(),他保证每个元组的键保持不变。
所有这些,下面是输出RDD上设置分区的所有操作:cogroup(),groupWith(),join(),leftOuterJoin(),rightOuterJoin(),groupByKey(),reduceByKey(),combineByKey(),partitionBy(),sort(),mapValues(),flatMapValues()和filter()。所有的其他操作都不会产生分区。
最后,对于二进制操作,分区的设置依赖于父的RDD的分区。默认的是hash分区,分区的个数设置为并行操作的等级。然而,如果其中一个父RDD有分区,那么它就是那个分区,如果父RDD都有分区,他将是第一个父的分区。