一、概念介绍
1.RDD
RDD(Resilient Distributed Dataset):弹性分布式数据集,是Spark中最基础的数据抽象。它本质就是一个类,屏蔽了底层对数据的复杂抽象和处理,为用户提供了一组方便数据转换和求值的方法。
简单来说,就是Spark把处理的数据会封装成一个个的数据集,这个数据集是会进行分区的,就是将数据均分,每个分区的数据用一个线程进行处理。所以要理解,Spark是将整个数据切分成多个分区,以启动时指定的核数(如果是使用了超线程技术的核,那处理任务的并行度就是核数的2倍)并行处理数据,例:总共20核,50个分区,那么并行度只有20,要三轮才能处理完所有的数据,最后一轮有10个核是空转的状态。
2.Driver
每个Spark任务的任务分配、协调的角色。
3.Executor
每个Spark任务真正进行数据计算的角色。
图1
二、资源设置指导
1.主要设置参数:
(1) executor-cores 执行器的核数,spark-submit脚本中指定
如图1所示,该参数设置的是启动的每个执行器的核数,每个执行器都是一个JVM程序,设置核数个数就是该JVM启动的处理线程的个数。
(2) executor-memory 执行器的内存个数,spark-submit脚本中指定
执行器内存,每个执行器JVM内存容量,该内存是由执行器分配的核数存储资源所共享的,计算资源不共享
(3) total-executor-cores 总的执行器核数,spark-submit脚本中指定
总执行器核数数量,该值除以每个执行器的核数,就等于需要启动的执行器数量,该值为Spark任务总的并行度。
(4) number-partition 分区个数,后端传入参数指定
分区个数,该值为程序内使用,数据经过一系列的转换操作后,可能每个分区内的数据数量就不相近了,会使用repartition进行重分区,以使每个分区的数据量重新均分。通过后端传入json参数的sparkConfig.numPartitions参数指定
2.设置建议(重点)
每个执行器设置核数和内存个数时,要基于总待处理数据量及分区个数进行估算,如果总数据量60G,分区个数10,则每个执行器每个线程所分配的内存应该尽可能是6G,如果每个执行器线程分配的内存为3G,则Spark处理完单个分区中3G数据后发现内存不够,会根据优先级删除过期的数据,再开始加载未处理的3G数据。此时虽然标记了删除已处理的数据,然而JVM-GC并未真正开始,对象未真正删除,再加载未处理的数据进行计算,就容易导致OOM task failed。
(1) total-executor-cores设置个数
公式:number-partition = total-executor-cores ✖️ n
n最好为整数,避免最后一轮存在执行器空转。一般为2~3,n越大,每个分区处理的数据量越小,处理的分区轮数越多,需要合理设置
(2) executor-cores与executor-memory
关于executor-memory的内存模型以及executor-cores的内存使用看下图:
公式:(executor-memory - 300mb) x 0.6 x 0.5 / executor-cores = 总待处理数据的数据总量 / number-partition
例如:此时待处理的数据总量是1000GB, 此时的 number-partition[分区数] = 1000,则单个task计算所需使用量为:1gb
按照spark官网推荐:spark应用的最大并行度应为total-executor-cores的2~3倍,故total-executor-cores应该设置为1000/[3或2] 约等于:300 ~ 500 左右,为了方便计算设置 total-executor-cores = 300,executor-cores = 3,executor个数则为100,根据上面的公式反向推导,该executor-memory = 1gb * 3 / 0.5 / 0.6 + 300mb = 10.5gb
T为单个分区分配数据量对于单个线程分配内存的倍数,建议1~3倍。每个线程分配的内存一般是3~6G。
(3) number-partition
分区个数的设置参考(1)(2)中的公式。
3.结论
该四项值设置为基础设置,需要综合考虑设置合理范围!