首页 > 代码库 > spark性能调优04-算子调优
spark性能调优04-算子调优
1、使用MapPartitions代替map
1.1 为什么要死使用MapPartitions代替map
普通的map,每条数据都会传入function中进行计算一次;而是用MapPartitions时,function会一次接受所有partition的数据出入到function中计算一次,性能较高。
但是如果内存不足时,使用MapPartitions,一次将所有的partition数据传入,可能会发生OOM异常
1.2 如何使用
有map的操作的地方,都可以使用MapPartitions进行替换
/** * 使用mapPartitionsToPair代替mapToPair */ JavaPairRDD<String, Row> sessionRowPairRdd =dateRangeRdd.mapPartitionsToPair(new PairFlatMapFunction<Iterator<Row>, String, Row>() { private static final long serialVersionUID = 1L; public Iterable<Tuple2<String, Row>> call(Iterator<Row> rows) throws Exception { List<Tuple2<String, Row>> list=new ArrayList<Tuple2<String, Row>>(); while (rows.hasNext()) { Row row=rows.next(); list.add(new Tuple2<String, Row>(row.getString(2), row)); } return list; } }); /*JavaPairRDD<String, Row> sessionRowPairRdd = dateRangeRdd .mapToPair(new PairFunction<Row, String, Row>() { private static final long serialVersionUID = 1L; // 先将数据映射为<sessionId,row> public Tuple2<String, Row> call(Row row) throws Exception { return new Tuple2<String, Row>(row.getString(2), row); } });*/
2、使用coalesce对过滤后的Rdd进行重新分区和压缩
2.1 为什么使用coalesce
默认情况下,经过过滤后的数据的分区数和原分区数是一样的,这就导致过滤后各个分区中的数据可能差距很大,在之后的操作中造成数据倾斜
使用coalesce可以使过滤后的Rdd的分区数减少,并让每个分区中的数据趋于平等
2.2 如何使用
//过滤符合要求的ClickCategoryIdRow
filteredSessionRdd.filter(new Function<Tuple2<String,Row>, Boolean>() { private static final long serialVersionUID = 1L; public Boolean call(Tuple2<String, Row> tuple2) throws Exception { return (Long.valueOf(tuple2._2.getLong(6))!=null)?true:false; } }) //使用coalesce将过滤后的数据重新分区和压缩,时新的分区中的数据大致相等 .coalesce(100)
3、使用foreachPartition替代foreach
3.1 为什么使用foreachPartition
默认使用的foreach,每条数据都会传入function进行计算;如果操作数据库,每条数据都会获取一个数据库连接并发送sql进行保存,消耗资源比较大,性能低。
使用foreachPartition,会把所用partition的数据一次出入function,只需要获取一次数据库连接,性能高。
3.2 如何使用
/** * 使用foreachPartition替代foreach */ sessionRdd.join(sessionRowPairRdd).foreachPartition(new VoidFunction<Iterator<Tuple2<String,Tuple2<String,Row>>>>() { private static final long serialVersionUID = 1L; public void call(Iterator<Tuple2<String, Tuple2<String, Row>>> iterator) throws Exception { List<SessionDetail> sessionDetails=new ArrayList<SessionDetail>(); if (iterator.hasNext()) { Tuple2<String, Tuple2<String, Row>> tuple2=iterator.next(); String sessionId=tuple2._1; Row row=tuple2._2._2; SessionDetail sessionDetail=new SessionDetail(); sessionDetail.setSessionId(sessionId); sessionDetail.setTaskId((int)taskId); sessionDetail.setUserId((int)row.getLong(1)); sessionDetails.add(sessionDetail); } DaoFactory.getSessionDetailDao().batchInsertSessionDao(sessionDetails); } }); /* sessionRdd.join(sessionRowPairRdd).foreach(new VoidFunction<Tuple2<String,Tuple2<String,Row>>>() { private static final long serialVersionUID = 1L; public void call(Tuple2<String, Tuple2<String, Row>> tuple2) throws Exception { String sessionId=tuple2._1; Row row=tuple2._2._2; SessionDetail sessionDetail=new SessionDetail(); sessionDetail.setSessionId(sessionId); sessionDetail.setTaskId((int)taskId); sessionDetail.setUserId((int)row.getLong(1)); DaoFactory.getSessionDetailDao().insertSessionDao(sessionDetail); } });*/
4、使用repartition进行调整并行度
4.1 为什么要使用repartition
spark.default.parallelism设置的并行度只能对没有Spark SQL(DataFrame)的阶段有用,对Spark SQL的并行度是无法设置的,该并行度是通过hdfs文件所在的block块决定的。
可以通过repartition调整之后的并行度
4.2 如何使用
sqlContext.sql("select * from user_visit_action where date >= ‘" + startDate + "‘ and date <= ‘" + endDate + "‘").javaRDD() //使用repartition调整并行度 .repartition(100)
5、使用reduceByKey进行本地聚合
5.1 reduceByKey有哪些优点
reduceByKey相对于普通的shuffle操作(如groupByKey)的一个最大的优点,会进行map端的本地聚合,从而减少文件的输出,减少磁盘IO,网络传输,内存占比以及reduce端的聚合操作数据。
5.2 使用场景
只有是针对每个不同的key进行相应的操作都可以使用reduceByKey进行处理
spark性能调优04-算子调优