Spark Tuning概述:
Spark Program的瓶颈:
CPU:
memory:
CPU和memory合称为资源
network bandwidth:
Spark哪些地方可以优化:
storing RDDs in serialized form to decrease memory usage
以序列化的方式存储减少内存的使用
memory:
内存调优:
1. 对象占用了多少内存 cache测试一下
2. 访问对象花费的内存
By default, Java objects are fast to access,
but can easily consume a factor of 2-5x more space than
the “raw” data inside their fields
3. GC (garbage collection)
Memory Management
Execution: shuffles, joins, sorts and aggregations
Storage: cache
统一内存管理:
两者能互相借内存
但是,执行可以强行占用存储的内存
存储不能强行占用执行的内存 (强行的意思是,两者都在工作的时候,
如果必要,执行的时候内存不够了,可以不管storage,直接拿它的内存)
Spark1.5之前默认采用的是StaticMemoryManager,exe的最大安全内存和storage的最大安全内存都是固定的,调优的话需要手动去调优
Spark1.6之后默认采用的是UnifiedMemoryManager
UnifiedMemoryManager
// 假如系统内存10G 系统默认的reservedMemory是300M
val usableMemory = systemMemory - reservedMemory //10G-300M
//默认内存最大使用百分之60
val memoryFraction = conf.getDouble("spark.memory.fraction", 0.6)
//(10G-300M)*0.6
(usableMemory * memoryFraction).toLong
// 最大存储所占内存占0.5 所以最大运行内存也占0.5
onHeapStorageRegionSize =
(maxMemory * conf.getDouble("spark.memory.storageFraction", 0.5)).toLong,
GC:
Serialization:
Java:
large
slow
Kryo(spark2.4对应的是version 4):
并不支持all serializable types
需要注册 register the classes
quickly
compact(紧凑,小)
如果没注册
就会慢一些,大一些
更换序列化方式:
没有必要写到代码里
建议写到 $SPARK_HOME/conf/spark-default
也可以 ./spark-submit --conf key=value (优先级高一些)
测试数据占用内存:
data==>rdd.cache 默认采用MEMORY_ONLY
data==>rdd.cache() 采用MEMORY_ONLY_SER
然后将spark.serializer 更改为Kryo
data==>rdd.cache() 采用MEMORY_ONLY_SER
再regist后
data==>rdd.cache() 采用MEMORY_ONLY_SER
查看RDD占用内存
1. put rdd into cache,look at the Storage
2. 在代码中使用SizeEstimator类的estimate方法
SizeEstimator.estimate(path) 为啥大了那么多???
将文件变成RDD,本身会扩大2-5倍
但是上面这个estimate为啥会接近10倍
并行度:
textFile 中的参数 minPartition 可以改用以提升并行度
groupByKey,reduceByKey(在PairRDDFunction)
总体来说:
set the config property spark.default.parallelism to change the default
可以在配置文件中更改 spark.default.parallelism 来改变默认的并行度
推荐一个core跑两到三个task(不管是实际core还是虚拟core)
广播变量:
减少每个task的内存(增加并行度):
分区太大,一个分区装的数据太多,可能会导致OOM
需要通过增加并行度的方式来解决,这样可以使每个分区的数量减少。
但是这种增加并行度的方式解决不了数据倾斜
数据本地性:
当一个作业申请到资源以后,需要Driver端将code发到Executor中去,之后需要在Executor上执行操作。
如果Executor执行的这个代码和他操作需要的数据在一个节点上,这个计算的速度就会很快,效率也会很高
数据本地性是指code和data要尽可能近,最好是能在一个节点(理想)
tips:一个作业在申请完资源后,就被定到一台机器上了,正常情况下之后再移动的只能是数据
数据和code的位置关系:
PROCESS_LOCAL(最佳) : 两者在一个JVM里,是一个进程的操作;这种情况是数据已经cache到executor中了(executor:run tasks and cache data)
NODE_LOCAL: (一台机器)数据在HDFS上,作业在Executor上;跑的是两个进程
RACK_LOCAL:
ANY: