使用广播变量
广播变量原理
广播变量,在初始时就在driver上有一个副本。Task在运行时要使用广播变量中的数据,此时首先会在自己本地的Executor对应的BlockManager中尝试获取变量的副本;如果本地没有,那么就从driver远程拉取变量副本,并保存在BlockManager中,此后这个executor上的task都会直接使用本地的BlockManager中的副本。
Executor的BlockManager除了从driver上拉取,也可能从其他节点的BlockManager上拉取变量副本,越近越好。
广播变量的好处
不是每个task一份副本,而是每个executor中所有的task共用一份副本。这样就可以让产生的副本大大减少,减少IO消耗。
使用Kryo序列化机制
默认情况下,spark内部使用的是java的序列化机制,ObjectOutputStream/ObjectInputStream,对象输入输出流机制来进行序列化。这种序列化机制好处在于处理方式比较简单;只需要我们在算子里面使用变量,必须实现Serializable接口即可实现序列化。
但缺点是:默认的序列化机制效率不高,序列化速度比较慢;序列化后的数据占用空间比较大。
Spark支持使用Kryo序列化机制。Kryo序列化机制比Java序列化机制,速度要快,序列化后的数据要更小,大概是java序列化机制的1/10。
何时使用kryo机制
注意:在 pom.xml下加入以下配置
<dependency>
<groupId>fastutil</groupId>
<artifactId>fastutil</artifactId>
<version>5.0.9</version>
</dependency>
//使用缓存和闭包时
SparkConf().set("spark.serializer","org.apache.spark.serializer.KryoSerializer")
//编写自定义类时
SparkConf().registerKryoClasses(new Class[]{CategorySortKey.class})
数据本地化
五个级别
- PROCESS_LOCAL
task要计算的数据在本进程(Executor)的内存中。
- NODE_LOCAL
①task所计算的数据在本节点所在的磁盘上。
②task所计算的数据在本节点其他Executor进程的内存中。
- NO_PREF
task所计算的数据在关系型数据库中,如mysql。
- RACK_LOCAL
task所计算的数据在同机架的不同节点的磁盘或者Executor进程的内存中
- Any
跨机架
Spark中任务调度时,TaskScheduler在分发之前需要依据数据的位置来分发,最好将task分发到数据所在的节点上,如果TaskScheduler分发的task在默认3s依然无法执行的话,TaskScheduler会重新发送这个task到相同的Executor中去执行,会重试5次,如果依然无法执行,那么TaskScheduler会降低一级数据本地化的级别再次发送task。
如上图中,会先尝试1,PROCESS_LOCAL数据本地化级别,如果重试5次每次等待3s,会默认这个Executor计算资源满了,那么会降低一级数据本地化级别到2,NODE_LOCAL,如果还是重试5次每次等待3s还是失败,那么还是会降低一级数据本地化级别到3,RACK_LOCAL。这样数据就会有网络传输,降低了执行效率。
如何提高数据本地化的级别?
可以增加每次发送task的等待时间(默认都是3s),将3s倍数调大,
注意:参数不是固定的,有时参数调大了反而会影响性能,结合WEBUI来使用
spark.locality.wait
spark.locality.wait.process
spark.locality.wait.node
spark.locality.wait.rack