文章目录
数据量很大时,单个节点无法完全存储和计算,需要分割成多个数据块Block,Spark自动对RDD中的大量元素进行分区,并分配给Worker节点做计算。Spark的分区是逻辑上的分区。执行过程中有多少个Task是由RDD分区决定的。同时运行几个Task是由分配给Executor的Core的数量决定的。
RDD Partition属性
partitions
返回包含RDD所有分区引用的一个数组
partitions.size
返回RDD的分区数量
partitioner
返回下列之一:None、HashPartitioner、RangePartitioner、CustomPartitioner(自定义分区器)
分区数量在集群环境读取HDFS文件时,由数据的block个数决定,最小为2。
RDD的初始分区
spark.default.parallelism
spark默认并行度可以推算出RDD的分区数。
如果SparkConf中设置了 spark.default.parallelism那么spark.default.parallelism就等于设置的值。一般设置为Executor的core的总数的2-3倍。如executorsexecutor-cores3
如果没有设置
1)Local模式下:local = 1
local[4] = 4
2 ) Yarn和Standalone模式下:
等于所有Executor能使用的core的总数
通过RDD产生方式计算RDD分区数
第一种方式:集合创建。分区数=spark.default.parallelism
val rdd = sc.parallelize(1 to 100)
第二种方式:从HDFS读取。分区数=HDFS文件的block数
第三种方式:通过已有RDD生成。分区数遵循遗传特性。
有多少个分区是合适的?
从HDFS上读取的文件分区数可以在后续进行重分区。
分区太少会有空闲资源没有利用,容易产生数据倾斜;
分区太多会有频繁的资源切换,任务调度可能比执行时间还长。
一般设置为可用core数量的2-3倍,如executorsexecutor-cores3。
(大数据集群配置:CPU核心数36-48、内存128M或256M)
Spark分区器
Spark有两种分区器:HashPartitioner和RangePartitioner。
HashPartitioner
基于key.hashcode()实现,保证正数。
public class HashPartitioner<K2, V2> implements Partitioner<K2,V2> {
public HashPartitioner(){
}
public void configure(JobConf job){
}
public int getPartition(K2 key, V2 value, int numReduceTasks){
return (key.hashCode() & 2147483647) % numReduceTasks;
}
}
RangePartitioner
如果有可排序的记录,将根据key对记录排序,然后根据范围划分。
代码示例
rdd.partitionby(new HashPartitioner(4)).saveAsTextFile("out/hashPartition4")
关于减少分区,有需要在filter之后(有空的Partition)进行重分区。
rdd.coalesce(2).saveAsTextFile("out/hashPartition2")
coalesce只能减少分区,coalesce对性能做了优化,尽可能减少shuffle。