Spark调优解决方案(七)之算子调优

一、算子调优之MapPartitions
---------------------------------------
    1.spark中最基本的原则就是每个task处理一个RDD中的partition数据

    2.mappartitions操作的优点
        如果是普通的map,比如一个分区中有1万条数据,那么你的map里面的function要执行1万次
        如果,应用了MapPartitions操作之后,一个task之执行一次function,这一次function
        会接受全部的1万条数据,一次性处理,性能较高

    3.MapPartitons的缺点
        数据全部传入一个map函数中,对内存的要求很高,很可能造成内存溢出

    4.何时使用MapPartitons?
        数据量不是特别大的时候,都可以用这种MapPartitions系列操作,性能还是非常不错的,是有提升的。
        但是也有过出问题的时候,MapPartitions只要一用,直接OOM,内存溢出,崩溃,如果出现,就放弃吧

    5.actionRDD.mapToPair() ---->  actionRDD.mapPartitionsToPair()


二、算子调优之filter过后使用coalesce算子减少分区数量
---------------------------------------------------
    1.默认数据经过filter算子之后,RDD的每个partition的数据量就可能不一样,甚至相差很多
    而且整体的数据量也变少了,如果还是使用原来数量的task,就有点浪费资源了

    2.举个例子,过滤前RDD的每个分区的数据量是1000,所有分区都差不多,过滤完之后,分区的数据量
    就变成了 300,100,900, ...这个时候,每个task处理的数据量不同,同样的处理逻辑,
    100的分区处理速度是900分区的9倍,很容易就造成了数据倾斜

    3.针对这两个问题,解决方案就是:使用coalesce减少分区数量
        使用coalesce减少分区数量,本质上就是对分区数据进行再分配,压缩分区数据。比如以前4个分区
        过滤掉了大半数据,之后完全可以压缩成2个partition的数据,并且尽量保证分区数据的均衡
        如此,即节省了task的数量,又从新平衡了partitoion的数据量,避免了数据倾斜

    4.actionRDD.filter(new function()).coalesce(int numpartiitons)


三、算子调优之使用foreachPartition算子优化写数据库性能
----------------------------------------------------
    1.对于往数据库中写入数据,默认采用的是foreach方式,一次写入一个分区的一条数据。这样的缺点就是
    对于每条数据都要单独去调用一次function,然后为每一条处理后的数据,创建一个数据库连接,插入数据
    完毕后,然后销毁连接。
    如果100万条数据,那么就要执行100万次函数,建立100万次数据库连接,同时也销毁100万次。即使有数据库
    连接池,那么也照样得发送100万次sql到数据库,非常耗性能

    2.所以,入库算子一般使用的都是foreachPartition算子,类似于MapPartitions算子,一次性处理一个分区
    的数据,将一个分区的数据一次性执行function,创建一次数据库连接,发送一次sql语句,将整个分区的数据
    一次性插入到数据库中,然后销毁一次连接

    3.对于foreachPartition算子,当然也有问题,类似于MapPartiiton算子,如果一次性处理的数据量太大,那么
    很可能就内存溢出了。如果真是一个分区100万条记录,那么基本是废了。但是如果一个分区是1000条数据,那么
    性能提升还是很大的

    4.actionRDD.foreach( //里面是sql提交内容  )  ----> actionRDD.foreachPartitions(  //里面是sql批处理提交内容)


四、算子调优之使用repartition解决Spark SQL低并行度的性能问题
----------------------------------------------------------
    1.以前说过,并行度是可以手动设定的: 设置spark.default.parallelism
    或者textFile() 传入第二个参数,指定partition数量

    2.设置的这个并行度,在哪些情况下会生效?哪些情况下,不会生效?
      如果你压根儿没有使用Spark SQL(DataFrame),那么你整个spark application默认所有stage的
      并行度都是你设置的那个参数。(除非你使用coalesce算子缩减过partition数量)
      换句话说,用Spark SQL的那个stage的并行度,你没法自己指定,而是默认会根据HDFS上的block数据块的
      数量去设置task的数量。比如你的并行度设定的1000,但是SparkSQL查询的数据在hdfs上占用了20块block
      那么,在sparksql的运行中,使用的就是20个task,去生成DataFrame.而且直接导致,与sparksql在同一个
      stage中的其他算子操作,也必须只能使用20个task了。

    3.如何解决上述Spark SQL无法设置并行度和task数量?
        使用repartition算子,
        你用Spark SQL这一步的并行度和task数量,肯定是没有办法去改变了。
        但是呢,可以将你用Spark SQL查询出来的RDD,使用repartition算子,去重新进行分区,
        此时可以分区成多个partition,比如从20个partition,分区成100个。
        然后呢,从repartition以后的RDD,再往后,并行度和task数量,就会按照你预期的来了。
        就可以避免跟Spark SQL绑定在一个stage中的算子,只能使用少量的task去处理大量数据
        以及复杂的算法逻辑。

    4.sqlContext.sql().javaRDD().repartition(100);


五、算子调优之reduceByKey本地聚合介绍
-----------------------------------------------------
    1.reduceByKey,相较于普通的shuffle操作(比如groupByKey),它的一个特点,
    就是说,会进行map端的本地聚合。

    2.stage0 的map端,会为stage1的每个task准备一份数据,如果接下来的是reduceByKey的shuffle操作
    那么就会在stage0的map进行combaine操作,提前执行reduce的逻辑,以在map端就减少数据量

    3.reduceByKey的这种机制,对性能有哪些提升?
        a.在本地进行聚合以后,在map端的数据量就变少了,减少磁盘IO。而且可以减少磁盘空间的占用。
        b.下一个stage,拉取数据的量,也就变少了。减少网络的数据传输的性能消耗。
        c.在reduce端进行数据缓存的内存占用变少了。
        d.reduce端,要进行聚合的数据量也变少了。

    4.所以,在能使用reduceByKey的情况下,还是首选的,较于其他的shuffle算子












































































  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值