一、算子调优之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算子
spark repartition 100 spark repartition 10000条一批
转载本文章为转载内容,我们尊重原作者对文章享有的著作权。如有内容错误或侵权问题,欢迎原作者联系我们进行内容更正或删除文章。
提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
相关文章
-
Spark coalesce和repartition
Spark coalesce和repartition
spark 不执行 apache