Spark性能问题及调优方法

       Spark性能问题及调优方法

1.Spark算子调优最佳实践

  • 1.1 使用mapPartitions取代map操作

    • 如果在映射过程中需要频繁创建额外的对象,使用mapPartitions要比使用map高效。
      例1:将RDD中的所有数据通过JDBC连接写入数据库,如果使用map函数,那么每条数据都需要创建一个连接,开销很大;而如果使用mapPartitions,则只需要每个分区创建一个连接即可。
      例2:每条数据都要创建一个列表对象,而如果使用mapPartitions则只需要对每个分区创建一个列表对象。
      rdd.map(lambda x:[x[0],x[1]]).reduceByKey(lambda …)。
  • 1.2 使用foreachPartition取代foreach操作

    • 原理与map和mapPartitions一致。
  • 1.3 使用coalesce取代rePartition操作

    • 因为coalesce操作不会产生shuffle,而rePartition会。在分区数量变化不剧烈时,常使用coalesce。
      :对RDD通过filter过滤掉较多数据时,可以通过coalesce来合并小文件。rdd.filter(lambda x:…).coalesce(10)
  • 1.4 使用reduceByKey、AggregateByKey取代groupByKey操作

    • reduceByKey会先在本地对数据进行聚合,相较于groupByKey,可以减少I/O操作。
  • 1.5 使用广播变量 broadcast

    • 如果Executor需要访问Driver端的数据,在未使用广播变量时,有多少个task,则Driver端的数据就会被复制多少份;
    • 当使用广播变量时,一个Executor上只会有一个数据的副本。
    • :一个Application中,有10个Executor、100个Task,一个数据副本100M。如果不使用广播,则每个Task会得到一个数据副本,因此网络传输需要消耗100*100M的内存;而如果使用广播,则每个Executor会得到一个数据副本,因此网络传输需要消耗10*100M内存,对资源的消耗大大降低。
    • 注:广播变量的级别是只读。
  • 1.6 Join不产生Shuffle的原理

    • join不产生shuffle的算法称为map-side join,它应用在大表与小表的join,所谓的小表指的是能存放于内存的表。
      具体步骤:1.将小表通过broadcast广播给所有的Executor;
             2.在map端进行join。
  • 1.7 RDD的复用

    • Spark默认每次对一个RDD执行一个算子操作时都会从源头处计算一遍该RDD,当一个RDD在整个计算过程中需要被重复使用时,可以考虑对该RDD进行持久会操作,即将该RDD保存在内存或磁盘中,这样在下次复用时就可以直接提取该RDD的数据而不需要根据该RDD的血统,从源头开始重新计算该RDD了。
    • 进行持久化的算子有persist和cache。
      cache操作是persist的一个简易版本,它默认将RDD持久化到内存中;而persist是一个手动持久化算子,它可以手工选择持久化级别,包括是持久化到内存还是硬盘、是否序列化以及序列化的方式。
      序列化操作的作用是减少RDD占用的内存或磁盘空间,避免发生频繁的垃圾回收(GC)。但是序列化要比不序列化耗时,采取什么序列化方式也是一个调优方式。类似的,持久化也需要花费一定的时间,因此,除非RDD重复使用,否则即便内存再充足也不应随便持久化。

2.Spark频繁遇到的性能问题及调优技巧

  • 2.1 对于Driver端的数据通过广播变量进行分发
    • 原理即为1.5的说明
  • 2.2 使用Kryo序列化代替Java序列化(默认)
    • Spark默认的序列化方式是Java序列化,Java序列化非常灵活,但是速度较慢,在某些情况下序列化的结果也比较大。
    • Spark 2.X版本可以使用Kryo序列化对象。Kryo序列化速度快,结果紧凑,但是不支持所有类型,为了提高性能,需要提前注册程序中使用的类(class),不注册速度性能好像极差。
    • 通过conf.set(“spark.serializer”,“org.apache.SPark.serializer.KryoSerializer”)来使用Kryo序列化,并通过conf.registerKryoClasses(Array(classOf[类1],classOf[类2])来进行注册。
  • 2.3 checkpoint的正确使用
    • checkpoint即检查点,是一个将某个中间RDD保存在本地文件夹或者HDFS的过程,该操作的目的是防止数据丢失。
    • checkpoint与cache或者persist的异同
      同:都是保存RDD的中间结果,防止RDD丢失导致数据重复计算。
      异:持久化(cache或persist)虽然是将RDD保存到内存或者硬盘当中,但是该保存的数据仍然是由blockManager管理,当Application进程结束以后,持久化的数据将会被清除(包括内存级别和硬盘级别);而checkpoint则是将RDD写入本地文件夹或者HDFS,当Application进程结束,数据仍然存在。
    • 调用checkpoint会触发一个job,因此在checkpoint之前,需要将需要checkpoint的RDD进行persist持久化,否则checkpoint完后,又得根据RDD的血统从源头开始重新计算一遍该RDD。

3. Spark集群资源分配及并行度调优

  • 3.1 Spark内存模型

    • 影响Executor内存的参数主要有两个:
      spark.executor.memory(堆内内存)
      spark.yarn.executor.memoryOverhead(堆外内存)
      内存申请时,应保证 堆内内存 + 堆外内存 <= yarn.scheduler.maximum-allocation-mb
    • 堆外内存:
      主要用于JVM运行的开销
    • 堆内内存:
      堆内内存被划分为:存储内存+计算内存+其它内存+保留内存
    • 1.存储内存用于存放持久化(cache、persist)及广播(broadcast)的数据;
    • 2.计算内存用于拉取上一个stage的输出,以及进行聚合等操作时使用;
    • 3.其它内存用于存放元数据等;
    • 4.保留内存用于spark对内存不精确计算时的溢出情况的优化。
  • 3.2 Spark并行度设置

    • 并行度(其实就是RDD的Partition数量,一个Partition对应一个Task)就是Spark作业中,各个阶段(stage)的Task的数量,也就代表了Spark在各个Stage的并行度。
    • 并行度的作用
      考虑一个并行度设置不当的情况:
      假设一个stage有100个Task,现有150个core的资源(spark中一个core同时处理一个task)。在计算时虽然有150core,但此时的并行度只能达到100,有50个core的资源将被闲置。如果将Task数量增加到150以上,这样就可以同时调动150个core进行计算。一般并行度设置为core总数的2-3倍。

4. Spark集群中Mapper端、Reducer端内存调优

  • 4.1 Shuffle过程中,Mapper端做了什么?

    • Mapper端主要就是为Reducer端提供数据,具体过程如下:
      1.map将RDD中的每一行数据转化为(key,value)的键值对形式,但并不会马上将该键值对写入到磁盘,而是放在了“环形内存缓冲区”,并在此刻给每个key-value对一个partitionId,当缓冲区内存达到阈值后,便将缓冲区的数据写入到磁盘的临时文件,写入前会对key值进行排序。
      2.当map全执行完后,此时每个节点上会有多个key-value的临时文件,因此需要对多个临时文件进行聚合,聚合时相同partitionId的文件合并到一起,并对各个partitionId中的key进行排序,到这一步,Mapper端的ShuffleWrite就完成了。
    • 调优:合理设置缓存层的大小,来避免频繁进行磁盘访问
      参数:spark.shuffle.file.buffer(可通过log信息来调整合理的buffer)
    • 注:Mapper端具体会将数据分成几个partition取决于Reducer端的并行度。
  • 4.2 Shuffle过程中的Reducer端

    • Mapper端已经对数据按key分好区了,并且会产生一个索引文件,记录分区的偏移量,Reducer只需要按照索引文件去Mapper端拉取自己所需要的数据即可。
  • 4.3 Sorted-Based Shuffle和Tungsten-Sorted Shuffle在Mapper和Reducer端的区别

    • Spark Sorted-Based Shuffle在Mapper端进行排序,包括Partition的排序和每个Partition内部元素的排序,但是Reducer端没有进行排序,所以Job的结果默认不是排序的。Sorted-Based Shuffle采用Tim-Sort排序算法,好处是可以极为高效地使用Mapper端的排序成果全局排序。
    • Tungsten-Sorted Shuffle在Mapper中不会对内部元素进行排序(它只会对Partition进行排序)。
    • Tungsten-Sorted Shuffle在程序有Aggregate的时候就退化为Sorted-Based Shuffle,或者是Mapper端输出的Partition大于16777216时,或者是一条Record大于128M时。
  • 4.3 内存性能调优

  1. Reducer端的业务逻辑(Business Logic)运行的空间不足,业务逻辑运行的时候被迫把数据溢出到磁盘上面,一方面造成了业务逻辑处理的时候需要读写磁盘令一方面也会导致不安全。
    解决办法:调整spark.shuffle.memoryFraction 。调整得越大,溢写到磁盘的次数就越少,效率越高。
  2. 发生Reducer端的OOM,Reducer端如果出现OOM,一般有内存中数据太多,无法容纳活跃的对象。
    解决办法:调小Reducer端的缓存层。该操作在减缓OOM的同时会导致Reducer向Mapper端拉取数据的次数变多,性能下降。
  3. shuffle file not found
    调优方法:可能是GC,当Executor进行GC时,所有的现成都停止工作,所以暂时无法获取数据。Reducer如果在此时想Mapper获取数据,可能会得不到响应,因此报错。解决办法就是增大响应的等待时间及重试次数。
    spark.shuffle.io.maxRetries
    spark.shuffle.io.retryWait

5.数据倾斜调优

  • 5.1 何为数据倾斜
    • 数据倾斜指的是在并行过程中,某个Task处理的数据量远远大于其它Task处理的数据量。
  • 5.2 数据倾斜对性能的影响
    • 1.一个最基本的影响就是,降低了计算效率。比如有10个Task,其中9个Task数据量很少,处理一个Task只需要1min,而有一个Task的数据量巨大,处理需要20min,那么整个计算任务在处理完9个小Task后还得继续等待第10个Task,效率急剧下降。
    • 2.比降低计算效率更坏的结果就是直接内存溢出(OOM)。1中虽然效率降低,但是好歹能计算出结果,而如果数据量大到直接报OOM,则整个程序连结果都得不到。
  • 5.3 导致数据倾斜的原因
    • 1.平时HDFS对数据块的划分一般都是均匀的,即等量分块。而在Shuffle过程中,相同key值的数据一般会分配给同一个Task进行运算,如果某个key的数据量过大,则在次阶段容易产生数据倾斜。
  • 5.4 处理办法
    • 1.如果数据量很大的key是一些无用的key,如null,-1的等,可以直接通过filter先过滤掉再进行计算。
    • 2.将数据倾斜发生过程上移到Hive端。Hive是底层封装了Hadoop的数据仓库处理工具,适合用于大数据集的批处理作业,将数据倾斜的操作前移到Hive中进行,在Hive中对数据倾斜的记录进行预处理,就可以从数据根源上解决了数据倾斜的问题。
    • 3.随机Key双重聚合
      随机Key双重聚合是指给每个key一个随机前缀,对key进行二次聚合。该方法适用于reduceByKey、GroupByKey等情景
      (1)第一次聚合(局部聚合):给每个key加上一个随机前缀,然后进行第一次聚合;
      (2)第二次聚合(双重聚合):去掉key的随机前缀,进行第二次聚合,得到的结果即为最终结果。
      可以从为什么reduceByKey要优于GroupByKey的角度来解析随机Key双重聚合的有效性
    • 4.提高Reducer端的并行度
      如果某个Task在处理时有100个key,且每个key的数据量都很大,要是在Reducer端提高并行度,把100个key分给10个Task,则此时每个Task处理的数据都将下降。(该方法不能解决单个key的数据倾斜问题)
      对于reduceByKey,可以传入参数numPartitions来指定并行度
    • 5.Join过程中用无Shuffle方法实现
      即1.6介绍,该方法避免了Shuffle,所以可以在一定程度上解决数据倾斜问题。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值