1、使用SparkConf配置Spark
在Scala中使用SparkConf创建一个应用
// 创建一个conf对象
val conf = new SparkConf()
conf.set("spark.app.name", "My Spark App")
conf.set("spark.master", "local[4]")
conf.set("spark.ui.port", "36000") // 重载默认端口配置
// 使用这个配置对象创建一个SparkContext
val sc = new SparkContext(conf)
SparkConf 实例包含用户要重载的配置选项的键值对。Spark中的每个配置选项都是基于字符串形式的键值对。
Spark允许通过spark-submit工具动态设置配置项。
//在运行时使用标记设置配置项的值
bin/spark-submit \
--class com.example.MyApp \
--master local[4] \
--name "My Spark App" \
--conf spark.ui.port=36000 \
myApp.jar
一旦传给了SparkContext的构造方法,应用所绑定的SparkConf就不可变了。
Spark程序中,默认配置优先级最高为set方法,其次是spark-submit,再次是写在配置文件中的值,最后是系统的默认值。
常用的Spark配置项
选项 | 默认值 | 备注 |
spark.executor.memory(–executor-memory) | 512m | 为每个执行器进程分配内存 |
spark.executor.cores(–executor-cores) | 1 | YRRN模式下,分配给任务的核心数目 |
spark.cores.max(–total-executor-cores) | 独立模式和Mesos模式下,设置执行器进程使用的核心上限 | |
spark.speculation | false | 设置为true时,开启任务预测执行机制,当出现速度比较慢的嗯五十,这种机制会在另外的节点上尝试执行该任务的一个副本,帮助减少个别较慢的任务带来的影响 |
spark.storage.blockmanagerTimeoutInterValMs | 45000 | 内部用来通过超市机制追踪执行器进程是否存货的阈值。对于会引发长时间垃圾回收(GC)暂停的作业,需要将这个值调到100000(100秒)以上来防止失败 |
spark.executor.extraJava | 空 | 自定义如何启动执行器进程的JVM,添加额外的java参数 |
spark.executor.extraClassPath | 空 | 自定义如何启动执行器进程的JVM,添加额外的ClassPath |
spark.executor.extra.LibraryPath | 空 | 自定义如何启动执行器进程的JVM,添加额外的程序库路径 |
spark.serializer | org.apache.spark.serializer.JavaSerializer | 指定用来进行序列化的类库,包括通过网络传输数据或缓存数据时的序列化。默认java库很慢,可以使用org.apache.spark.serializer.KryoSerializer并对Kryo进行适当的调优 |
spark.[X].port | 任意值 | 用来设置运行Spark应用是用到的各个端口。X:driver,fileserver,broadcast,replClassServer,blockManager以及executor |
spark.eventLog.enabled | false | 设为true时,开启事件日志。 |
spark.eventLog.dir | file:///tmp/spark-events | 开启事件日志时,事件日志的存储位置。 |
几乎所有Spark配置都发生在SparkConf的创建过程中,但你需要将conf/spark-env.sh中的环境变量SPARK_LOCAL_IDRS设置为用逗号隔开的存储位置列表,来指定SPark用来混洗的本地存储路径,这需要在独立模式和Mesos模式下设置。
2、Spark执行的组成部分:作业、任务和步骤
//在Scala版本的Spark shellz中处理文本数据
// 读取输入文件
scala> val input = sc.textFile("input.txt")
// 切分为单词并且删掉空行
scala> val tokenized = input.
| map(line => line.split(" ")).
| filter(words => words.size > 0)
// 提取出每行的第一个单词(日志等级)并进行计数
scala> val counts = tokenized.
| map(words = > (words(0), 1)).
| reduceByKey{ (a,b) => a + b }
这一系列命令生成了一个叫做counts的RDD,其中包含各级别日志对应的条目数。shell执行完这些命令后,程序没有执行任何行动操作。程序定义了一个RDD独享的有向无环图,每个RDD维护了其只想一个或多个父节点的引用,以及表示其与父节点之间关系的信息。这些引用可以使得RDD追踪到其所有的祖先节点。
RDD图与执行步骤的对应关系并不一定是一一对应的,当调度器进行流水执行,或把多个RDD合并到一个步骤中时。当RDD不需要混洗数据就可以从父节点计算出来时,调度器就会自动进行流水线执行。
当一个RDD已经缓存在集群内存或磁盘上时,Spark的内部调度也会自动截短RDD谱系图,直接基于缓存数据计算。还有一种截短发生在当RDD已经在之前的数据混洗中作为副产品物化出来时,及时没有被显示调用persist()方法。
特定的行动操作所产生的集合被称为一个作业,一旦步骤确定下来,任务就会被创建出来并发给内部的调度器。
一个物理步骤会启动很多任务,每个任务都是在不同的数据分区上做同样的事情。任务内部流程是一样的。
1、从数据存储或已有RDD或数据混洗输出中获取输入数据。
2、执行必要操作来计算这些操作所代表的RDD。
3、把输出写到一个数据混洗文件中,写入外部存储,或者发回驱动器程序。
spark 执行流程
1、用户代码定义RDD的有向无环图
2、行动操作把有向无环图强制转译为执行计划
一个作业包含一个或多个步骤,每个步骤是一波并行执行的计算任务。一个步骤对应有向无环图中的一个或读个RDDD(对应多个RDD是因为发生了流水线执行)。
3、任务于集群调动并执行
序列化格式
如果Spark需要通过网络传输数据,或将数据写到磁盘上,Spark需要把数据序列化为二进制。序列化在混洗时发生,使用Java的序列化库,同时也支持第三方序列化库Kryo,kryo可以提供比Java的序列化工具更短的序列化时间和更高压缩比的二进制表示,但不能直接序列化全部类型的对象,几乎所有的应用都在迁移到Kryo后获得更好的性能。
内存管理
RDD存储:调用RDD的persist()或cache方法,RDD分区会被存储到缓存中,Spark会根据spark.storage.memoryFraction限制缓存内存占整个JVM堆空间的比例大小超出限制,旧分区数据会被移除内存。
数据混洗与聚合的缓存区:进行数据混洗时,Spark会创建一些中间缓存区来存储数据清洗的中间数据以及输出数据。Spark会尝试根据Spark.shuffle.memoryFraction限定缓存区内存占总内存的比例。
用户代码:Spark可以执行任意的用户代码,所以用户的函数可以自行申请大量内存。用户代码可以访问JVM堆空间中除分配给RDD存储和数据混洗存储以外的全部剩余空间。
默认情况下,60%空间存储RDD,20%空间存储数据混洗操作产生的数据,20%留给用户程序
Spark默认cache操作会以memory_only的存储等级持久化数据。RDD分区空间不够时,旧分区直接删除,再用到数据时重新计算。以MEMORY_AND_DISK的存储级别Persist()方法调用效果更好,内存中放不下的旧分区会被写入硬盘。
对于缓存序列化后的对象而非直接缓存,通过MEMORY_ONLY_SER或MEMORY_AND_DISK_SER的存储等级来实现这一点。
硬件供给
在各种部署模式下,执行器节点的内存可以通过spark.executor.memory配置或者spark-submit的–executor-memory标记来设置。YARN模式下,可以通过spark.executor.cores或–excecutor-cores标记来设置执行器节点的核心数。