更多RDD的信息參考:https://www.cnblogs.com/qingyunzong/p/8899715.html
分區
在Spark
程序中,RDD
是由SparkContext
上下文生成的,一個數據源只能生成一個RDD
對象(流處理場景中,指定多個消息源可以生成多個RDD
,存在DStream
中)。
RDD(Resilient Distributed Dataset)
是Spark
中最基本的數據抽象,它代表一個不可變、可分區、里面的元素可并行計算的集合。
分區(Partition)
,即數據集的基本組成單位。對于RDD
來說,每個分區都會被一個計算任務Task
處理,并決定并行計算的粒度。用戶可以在創建RDD
時指定RDD的分區個數
,如果沒有指定,那么就會采用默認值。默認值就是程序所分配到的CPU Core的數目
(取決于運行環境)。如果是從HDFS
中創建,默認為文件的數據塊數
。
分區劃分器
Spark
默認提供兩種劃分器:哈希
分區劃分器(HashPartitioner)和范圍
分區劃分器(RangePartitioner),且Partitioner
只存在(K, V)類型的RDD
中,對于非(K, V)類型的Partitioner
值為None
。
//從test.txt 構建rdd
JavaRDD<String> rdd = sc.textFile("test.txt");
System.out.println("初始分區劃分器:" + rdd.partitioner().toString());
輸出:初始分區劃分器:Optional.empty
HashPartitioner
是默認分區劃分器,他的原理是對于給定的key
,計算其hashCode
,并除于分區的個數取余,如果余數小于0
,則用余數+
分區的個數,最后返回的值就是這個key
所屬的分區ID
。但HashPartitioner
易造成分區內數據不均勻(跟key
的分布息息相關)。
RangePartitioner
分區劃分器可以解決數據分布不均勻問題,他能保證分區與分區之間是有序的,一個分區中的元素肯定都是比另一個分區內的元素小或者大,但是分區內的元素是不能保證順序的。簡單的說就是將一定范圍內的數映射到某一個分區內。
groupByKey()
默認采用哈希分區劃分器,當然也可以手動指定分區劃分器(包括自定義分區劃分器)
pairRDD.groupByKey(4); //默認哈希分區劃分器,并指定分區數=4
OR
pairRDD.groupByKey(new HashPartitioner(4)); //指定哈希分區劃分器,并指定分區數=4
對<K,V>
結構的RDD
,還可以手動使用分區劃分器,使用partitionBy(Partitioner partitioner)
函數
JavaPairRDD<String, Iterable<Integer>> groupRDD = pairRDD.groupByKey();
System.out.println("partitionBy前初始分區劃分器:" + groupRDD.partitioner().toString());
groupRDD.partitionBy(new HashPartitioner(3)); //手動使用分區劃分器
System.out.println("partitionBy后初始分區劃分器:" + groupRDD.partitioner().toString());
請注意:
如果rdd
當前分區劃分器與partitionBy
指定的劃分器相同,則不再進行分區劃分,因此上述代碼輸出為
partitionBy前初始分區劃分器:Optional[org.apache.spark.HashPartitioner@4]
partitionBy后初始分區劃分器:Optional[org.apache.spark.HashPartitioner@4]
為了證明partitionBy
指定HashPartitioner
分區器沒有生效,我們改變一下分區數,并打印
JavaPairRDD<String, Iterable<Integer>> groupRDD = pairRDD.groupByKey(2); //指定分區數2
System.out.println("partitionBy前分區數:" + groupRDD.getNumPartitions());
groupRDD.partitionBy(new HashPartitioner(4)); //指定分區數4
System.out.println("partitionBy后分區數:" + groupRDD.getNumPartitions());
輸出:
partitionBy前分區數:2
partitionBy后分區數:2
指定分區的方法
并行化創建(創建rdd時指定)。指定生成n
個分區的rdd
// 構造數據源
List<Integer> data = Arrays.asList(1, 2, 3, 4, 5);
//并行化創建rdd
JavaRDD<Integer> rdd = sc.parallelize(data,n);
文件中創建(創建rdd時指定)。指定生成n
個分區的rdd
//從test.txt 構建rdd
JavaRDD<String> rdd = sc.textFile("test.txt",n);
shuffle時指定。指定shuffle
后新的rdd
的分區數(n在最后)
JavaPairDStream<String, Integer> wordCounts = pairs.reduceByKey(new Function2<Integer, Integer, Integer>() {
public Integer call(Integer integer, Integer integer2) throws Exception {
return integer + integer2;
}
},n);
指定默認配置。請注意:
此方式僅對shuffle
后的rdd
有效。即如果沒有在創建rdd
時指定分區數,該配置不會修改初始rdd
的分區數,但是對shuffle
后的新rdd
有效。
補充:
我之前有個疑問就是如果不指定分區,shuffle
前和shuffle
后的分區是不是變化的,經過本地測試,答案是會變化
。
conf.set("spark.default.parallelism","n");
本地模式。貌似也只對并行化創建rdd
有效,本地demo
設置local[*]
,打印從文件中創建的rdd
分區數結果是2
。這種方式不用太在意,本地只是測試用。
new SparkConf().setMaster(local[n]); //n 表示具體的分區數
或
new SparkConf().setMaster(local[*]); //*表示使用cpu core 數
腳本模式。沒研究
- Spark-shell --conf <key>=<value>
- Spark-submit --conf <key>=<value>
綜上
建議直接在操作rdd
的函數中指定分區數,不僅優先級最高,而且保證準確性。