1. 资源优化
1). 集群搭建:
master节点的..../conf/spark-env.sh中配置:
SPARK_WORKER_CORES
SPARK_WORKER_MEMORY
2). 任务提交中设置参数
./spark-submit --master spark://node 001:7077 --class ... jar ...
--executor-cores
--executor-memory
--driver-cores
--driver-memory
--total-executor-cores
2. 并行度调节
1). sc.textFile("filePath" , numparttition) --java/scala
2). sc.parallelize(list,numparttition) //parallelize()函数将一个List列表转化为了一个RDD对象
--java/scala3). sc.makeRDD(list , numparttition) //makeRDD函数实现是依赖了parallelize函数的实现
--scala4). sc.parallelizePairs(list , nump) //jiang--java
5). rdd.repartition(num)/rdd.coalesce(num)
6). rdd.reduceByKey(xx , num)/rdd.groupByKey(xx,num)
7).spark.default.parallelism
8).自定义分区器
9).spark.sql.shuffle.partitions = 200
10).SparkStreaming:
Direct:topic中partiton个数一致,增大topic的分区数|读取dstream 进行重新分区
3. 代码优化
1). 尽量避免创建重复的RDD
RDD根本是一个数据集,需要从外部数据源中获取,创建多个RDD就会从相同的数据源中进行 , 多次读取,这对集群来说是一种资源的浪费, 在编程中应该尽可能服用一个RDD.
2). 对多次使用的RDD进行持久化
对多次使用的RDD进行一个持久化的操作(persist()), 这样减少对RDD的重复计算,提高效率 . Saprk对于执行多算子默认原理为,每次多一个RDD执行一个算子操作时, 都会从源头处计算一遍, 计算出那个RDD来,然后再对这个RDD执行你的算子操作。这种方式的性能是很差的。我们可以保留一个中间状态,使得RDD不必做一些重复而无用的操作。
cache() -- MEMORY_ONLY = persist() = persist(MEMORY_ONLY)
persist() -- MEMORY_ONLY | MEMORY_ONLY_SER | MEMORY_AND_DISK | MEMORY_AND_DISK_SER3). 广播大变量
当Executor端使用到Driver端的大变量时, 是将这个大变量的副本通过网路I/O传输 到每一个运行这个程序上的task上.如果这个变量足够大, 在网络I/O上消耗的时间也是十分可观的, 而且还需要占用很多的储存空间. 针对这种“大”的外部变量(100M,甚至1G),我们可以将变量广播,使这个变量的副本只存在于每个Executer中,这样就大大减少了变量的副本数,大大的提升了效率。
val list1 = ...
val list1Broadcast = sc.broadcast(list1)
rdd1.map(list1Broadcast...)4). 尽量少用collect() 数据量比较大的时候,尽量不要使用collect函数,因为这可能导致Driver端内存溢出问题。
5). Map join代替 reduce join
mapjoin的并行度高 , 可以有效的避免数据倾斜 , 就相当于使用广播变量+filter 代替join ;
mapjoin需要一个表所有数据加载到缓存中,不能太大
6). 尽量使用map端有预聚合的的算子
好处: 减少shuffle的数据量 , 减少数据拉取量 , 减少reduce端的聚合次数 .
预聚合算子: reduceByKey() , combineByKey() , aggregateByKey()7). 使用高性能算子
使用reduceByKey代替groupByKey
使用mapPartitions代替map
使用foreachPartition代替foreach
对RDD使用filter进行大量数据过滤后 , 使用coalesce减少分区
使用map端与聚合和算子
8). 数据结构
(1). 尽量使用原生数据类型代替字符串类型,每个java对象都有对象头 , 引用等额外信息 , 因此比较占用内存空间 .
(2). 尽量使用字符串代替对象 , 每个字符串内部都有一个字符串数组以及长度等额外信息 .
(3). 尽量用数组代替集合 . 集合类型,比如HashMap、LinkedList等,因为集合类型内部通常会使用一些内部类来封装集合元素,比如Map.Entry。9). 使用kryo 序列化: 节省内存资源
SparkSession.config("spark.serializer" , "org.apache.spark.serializer.KryoSerializer")
或者 Sparkconf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
.registerKryoClasses(new Class[]{SpeedSortKey.class})
4. 数据本地化:
PROCESS_LOCAL 进程本地化
NODE_LOCAL 节点本地化
NO_PREF 数据从哪里获取都一样
RACK_LOCAL 机架本地化
ANY 出机架数据本地化调节:
spark.locality.wait.node 3s
spark.locality.wait.process 3s
spark.locality.wait.rack 3s