大数据课程K9——Spark的调优方法

本文介绍了如何通过Kryo优化Spark的序列化性能,包括配置KryoSerializer、代码中使用Kryo,以及如何配置多临时文件目录、启用推测执行、避免collect操作和使用mapPartitions。还讨论了Spark的广播变量和计数器功能。
摘要由CSDN通过智能技术生成

文章作者邮箱:yugongshiye@sina.cn              地址:广东惠州

 ▲ 本章节目的

⚪ 掌握Spark的——更好的序列化实现;

⚪ 掌握Spark的——通过代码使用Kryo;

⚪ 掌握Spark的——配置多临时文件目录;

⚪ 掌握Spark的——启用推测执行机制;

⚪ 掌握Spark的——避免使用collect;

⚪ 掌握Spark的——RDD操作使用MapPartitions替代map;

⚪ 掌握Spark的——Spark共享变量;

一、Spark调优—上篇

1. 更好的序列化实现

Spark用到序列化的地方

1. Shuffle时需要将对象写入到外部的临时文件。

2. 每个Partition中的数据要发送到worker上,spark先把RDD包装成task对象,将task通过网络发给worker。

3. RDD如果支持内存+硬盘,只要往硬盘中写数据也会涉及序列化。

 默认使用的是java的序列化。但java的序列化有两个问题:

1. 性能相对比较低,

2. 序列化完二进制的内容长度也比较大,造成网络传输时间拉长。

业界现在选择更好的实现为 kryo,比java的序列化快10倍以上。而且生成内容长度也短。时间快,空间小,自然选择它了。

方法一:修改spark-defaults.conf配置文件。

设置:

spark.serializer  org.apache.spark.serializer.KryoSerializer

注意:用空格隔开。

方法二:启动spark-shell或者spark-submit时配置。

--conf spark.serializer=org.apache.spark.serializer.KryoSerializer

方法三:在代码中。

val conf = new SparkConf()

conf.set(“spark.serializer”,“org.apache.spark.serializer.KryoSerializer”)

备注:三种方式实现效果相同,优先级越来越高。

2. 通过代码使用Kryo

文件数据:

rose 23

tom 25

Person类代码:

class Person(var1:String,var2:Int)  extends Serializable{

  var name=var1

  var age=var2

}

MyKryoRegister类代码:

import org.apache.spark.serializer.KryoRegistrator

import com.esotericsoftware.kryo.Kryo

class MyKryoRegister extends KryoRegistrator {

  def registerClasses(kryo: Kryo): Unit = {

    kryo.register(classOf[Person])

  }

}

KryoDriver代码:

import org.apache.spark.SparkConf

import org.apache.spark.SparkContext

import org.apache.spark.storage.StorageLevel

object KryoDriver { 

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setMaster("local").setAppName("kryoTest")

      .set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")

      .set("spark.kryo.registrator", "cn.tedu.MyKryoRegister")

    val sc = new SparkContext(conf)   

    val data=sc.textFile("d://people.txt")   

    val userData=data.map { x => new Person(x.split(" ")(0),x.split(" ")(1).toInt) }   

    userData.persist(StorageLevel.MEMORY_AND_DISK)

    userData.foreach(x=>println(x.name))

  }

}

二、Spark调优—下篇

1. 配置多临时文件目录

spark.local.dir参数。当shuffle、归并排序(sort、merge)时都会产生临时文件。这些临时文件都在这个指定的目录下。那这个文件夹有很多临时文件,如果都发生读写操作,有的线程在读这个文件,有的线程在往这个文件里写,磁盘I/O性能就非常低。

怎么解决呢?可以创建多个文件夹,每个文件夹都对应一个真实的硬盘。假如原来是3个程序同时读写一个硬盘,效率肯定低,现在让三个程序分别读取3个磁盘,这样冲突减少,效率就提高了。这样就有效提高外部文件读和写的效率。怎么配置呢?只需要在这个配置时配置多个路径就可以。中间用逗号分隔。

spark.local.dir=/home/tmp,/home/tmp2

2. 启用推测执行机制

可以设置spark.speculation  true

开启后,spark会检测执行较慢的Task,并复制这个Task在其他节点运行,最后哪个节点先运行完,就用其结果,然后将慢Task 杀死。

3. 避免使用collect

我们在讲的时候一直强调,collect只适合在测试时,因为把结果都收集到Driver服务器上,数据要跨网络传输,同时要求Driver服务器内存大,所以收集过程慢。解决办法就是直接输出到分布式文件系统中。

4. 有些情况下,RDD操作使用MapPartitions替代map

map方法对RDD的每一条记录逐一操作。mapPartitions是对RDD里的每个分区操作。

rdd.map{ x=>conn=getDBConn.conn;write(x.toString);conn close;}

这样频繁的链接、断开数据库,效率差。

rdd.mapPartitions{(record:=>conn.getDBConn;for(item<-recorders;write(item.toString);conn close;}

这样就一次链接一次断开,中间批量操作,效率提升。

三、Spark共享变量

1. 概述

Spark程序的大部分操作都是RDD操作,通过传入函数给RDD操作函数来计算。这些函数在不同的节点上并发执行,但每个内部的变量有不同的作用域,不能相互访问,所以有时会不太方便,Spark提供了两类共享变量供编程使用——广播变量和计数器。

2. 广播变量

这是一个只读对象,在所有节点上都有一份缓存,创建方法是SparkContext.broadcast()。

案例:

scala> val broadcastVar = sc.broadcast(Array(1, 2, 3))

broadcastVar: org.apache.spark.broadcast.Broadcast[Array[Int]] = Broadcast(0)

scala> broadcastVar.value

res0: Array[Int] = Array(1, 2, 3)

注意,广播变量是只读的,所以创建之后再更新它的值是没有意义的,一般用val修饰符来定义广播变量。

3. 计数器

计数器只能增加,是共享变量,用于计数或求和。

计数器变量的创建方法是SparkContext.accumulator(v, name),其中v是初始值,name是名称。

案例:

scala> val accum = sc.accumulator(0, "My Accumulator")

accum: org.apache.spark.Accumulator[Int] = 0

scala> sc.parallelize(Array(1, 2, 3, 4)).foreach(x => accum += x)

scala> accum.value

res1: Int = 10

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值