概述
在《spark2原理分析-RDD的Partitioner原理分析》一文中,我們瞭解了分區器的基本概念,本文通過實際的例子來進一步理解分區器的概念,並學習如何使用分區器。
分區器的使用場景
分區器在類型爲(k,v)的RDD時使用。但不要頻繁的修改分區器,頻繁使用分區器可能會導致更多的shuffle操作。
HashPartitioner分區器的使用
- 準備類型爲(k,v)的RDD
我們通過parallelize生成(k,v)對的RDD數據,在spark-shell中代碼如下:
scala> import org.apache.spark.HashPartitioner
scala> val rdd2 = sc.parallelize(1 to 10000).map(x=>(x,x))
rdd2: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[1] at map at <console>:24
scala> rdd2.partitions.length
res2: Int = 8
scala> rdd2.partitioner
res0: Option[org.apache.spark.Partitioner] = None
由於我是以local[*]模式啓動的,而我的機器有8個核,所以默認的分區數是8。但從以上代碼可以看出,分區器爲None。
下面我們使用新的分區器:HashPartitioner來對數據進行重新分區:
- 使用HashPartitioner分區器重新分區
scala> val rdd3 = rdd2.partitionBy(new HashPartitioner(10))
rdd3: org.apache.spark.rdd.RDD[(Int, Int)] = ShuffledRDD[3] at partitionBy at <console>:26
scala> rdd3.partitioner
res9: Option[org.apache.spark.Partitioner] = Some(org.apache.spark.HashPartitioner@a)
scala> rdd3.partitions.length
res10: Int = 10
從以上例子中看出,我們使用自帶的HashPartitioner(10)分區器來重新分區數據,重新定義分區器後,分區數已經發生了變化。分區數變成了按HashPartitioner分區的分區數。
RangePartitioner分區器的使用
- 使用RangePartitioner分區器重新分區
scala> import org.apache.spark.RangePartitioner
import org.apache.spark.RangePartitioner
scala> val rdd3 = rdd2.partitionBy(new RangePartitioner(20, rdd2))
rdd3: org.apache.spark.rdd.RDD[(Int, Int)] = ShuffledRDD[4] at partitionBy at <console>:26
scala> rdd3.partitioner
res3: Option[org.apache.spark.Partitioner] = Some(org.apache.spark.RangePartitioner@49057ee6)
scala> rdd3.partitions.length
res4: Int = 20
從以上的代碼例子中可以看出,設置RangePartitioner分區器後,分區數變成了20個。
打印每個分區的數據
val parts = rdd3.partitions
for (p <- parts) {
val idx = p.index
val partRdd = rdd3.mapPartitionsWithIndex {
case(index:Int,value:Iterator[(String,Int)]) =>
if (index == idx) value else Iterator()
}
val dataPartitioned = partRdd.collect()
println("partition id:"+ idx, "elements: " + dataPartitioned.foreach(print))
}
通過這段代碼可以打印出每個分區的數據。
通過打印RangePartitioner重新分區後的數據可以看出,RangePartitioner分區後的數據基本上是按照數據範圍來劃分的。
小結
本文說明了分區器的使用。並通過修改分區器來驗證了分區器的作用。