携手创作,共同成长!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情
一、RDD持久化实战
scala代码
package com.strivelearn.scala
import org.apache.spark.{SparkConf, SparkContext}
/**
* @author strivelearn
* @version PersistRddScala.java, 2022年11月26日
*/
object PersistRddScala {
def main(args: Array[String]): Unit = {
//创建SparkContext
val conf = new SparkConf()
conf.setAppName("AccumulatorOpScala")
.setMaster("local")
val context = new SparkContext(conf)
// cache默认是基于内存持久化的
val dataRdd = context.textFile("/Users/strivelearn/Desktop/demo.txt").cache()
var startTime = System.currentTimeMillis()
var count = dataRdd.count()
println(count)
var endTime = System.currentTimeMillis()
//324
//第一次耗时:654
println("第一次耗时:" + (endTime - startTime))
startTime = System.currentTimeMillis()
count = dataRdd.count()
println(count)
endTime = System.currentTimeMillis()
//324
//第二次耗时:30
println("第二次耗时:" + (endTime - startTime))
}
}
java
package com.strivelearn.java;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.util.LongAccumulator;
import java.util.Arrays;
import java.util.List;
/**
* @author strivelearn
* @version PersistRddJava.java, 2022年11月26日
*/
public class PersistRddJava {
public static void main(String[] args) {
//1.创建sparkContext
SparkConf sparkConf = new SparkConf();
sparkConf.setAppName("PersistRddJava");
sparkConf.setMaster("local");
JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
String path = "/Users/strivelearn/Desktop/demo.txt";
JavaRDD<String> stringJavaRDD = javaSparkContext.textFile(path).cache();
long startTime = System.currentTimeMillis();
long count = stringJavaRDD.count();
long endTime = System.currentTimeMillis();
//第一次耗时:599
System.out.println("第一次耗时:" + (endTime - startTime));
startTime = System.currentTimeMillis();
count = stringJavaRDD.count();
endTime = System.currentTimeMillis();
//耗时26
System.out.println("第二次耗时:" + (endTime - startTime));
}
}
二、共享变量的工作原理
- 默认情况下,一个算子函数中使用到了某个外部的变量,那么这个变量的值会被拷贝到每个task中,此时每个task只能操作自己的那份数据变量的数据
- Spark提供了两种共享变量,一种是Broadcast Variable(广播变量),另一种是Accumulator(累加变量)
- Broadcast Variable会将使用到的变量,仅仅为每个节点拷贝一份,更大的用处是优化性能,减少网络传输以及内存消耗
- 通过调用SparkContext的broadcast()方法,针对某个变量创建广播变量(广播变量是只读的)可以通过广播变量的value()方法获取值
2.1、Broadcast Variable
scala
package com.strivelearn.scala
import org.apache.spark.{SparkConf, SparkContext}
/**
* @author strivelearn
* @version BroadcastOpScala.java, 2022年11月26日
*/
object BroadcastOpScala {
def main(args: Array[String]): Unit = {
//创建SparkContext
val conf = new SparkConf()
conf.setAppName("BroadcastOpScala")
.setMaster("local")
val context = new SparkContext(conf)
val dataRdd = context.parallelize(Array(1, 2, 3, 4, 5))
var variable = 2
//定义广播变量
val variableBroadcast = context.broadcast(variable)
//使用广播变量,调用其value
dataRdd.map(_ * variableBroadcast.value).foreach(println)
context.stop()
}
}
java
package com.strivelearn.java;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.broadcast.Broadcast;
import java.util.Arrays;
import java.util.List;
/**
* @author strivelearn
* @version BroadcastOpJava.java, 2022年11月26日
*/
public class BroadcastOpJava {
public static void main(String[] args) {
//1.创建sparkContext
SparkConf sparkConf = new SparkConf();
sparkConf.setAppName("BroadcastOpJava");
sparkConf.setMaster("local");
JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
//创建集合
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
JavaRDD<Integer> rdd = javaSparkContext.parallelize(integers);
Broadcast<Integer> broadcast = javaSparkContext.broadcast(2);
rdd.map(num -> num * broadcast.value()).foreach(res -> System.out.println(res));
javaSparkContext.stop();
}
}
2.2、Accumulator
- 用于多个节点对一个变量进行共享性的操作
- Accumulator只提供了累加的功能,在task中只能对Accumulator进行累加操作,不能读取它的值,只有Driver进程中才可以读取Accumulator的值
scala代码
package com.strivelearn.scala
import org.apache.spark.{SparkConf, SparkContext}
/**
* @author strivelearn
* @version AccumulatorOpScala.java, 2022年11月26日
*/
object AccumulatorOpScala {
def main(args: Array[String]): Unit = {
//创建SparkContext
val conf = new SparkConf()
conf.setAppName("AccumulatorOpScala")
.setMaster("local")
val context = new SparkContext(conf)
val dataRDD = context.parallelize(Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
//这种写法是错误的,因为foreach代码是在worker节点上执行的
//var total = 0和println是在Driver进程中执行
//所以无法实现累加操作
//并且foreach算子可能会在多个task中执行的,这样foreach内部实现的累加也不是最终全局累加的结果
// var total = 0
// dataRDD.foreach(num => total += num);
// println("total: " + total);
//所以需要用到累加变量
//1.定义累加变量
val sumAccumulator = context.longAccumulator
//2.使用累加变量
dataRDD.foreach(num => sumAccumulator.add(num))
println(sumAccumulator.value)
}
}
java代码
package com.strivelearn.java;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.util.LongAccumulator;
import java.util.Arrays;
import java.util.List;
/**
* @author strivelearn
* @version AccumulatorOpJava.java, 2022年11月26日
*/
public class AccumulatorOpJava {
public static void main(String[] args) {
//1.创建sparkContext
SparkConf sparkConf = new SparkConf();
sparkConf.setAppName("AccumulatorOpJava");
sparkConf.setMaster("local");
JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
//创建集合
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
JavaRDD<Integer> rdd = javaSparkContext.parallelize(integers);
LongAccumulator longAccumulator = javaSparkContext.sc().longAccumulator();
rdd.foreach(integer -> longAccumulator.add(integer));
System.out.println(longAccumulator.value());
}
}
三、sortByKey如何实现全局排序
java代码要实现排序,需要把所有的数据都加载到内存中,这样的效果会比较低。(可能还会发生内存溢出的情况)。sortByKey如何解决这个问题呢?
通过RangePartitioner可以实现根据数据范围来分区:P1中的所有数据小于P2。P2中的所有的数据小于P3,依次类推。(采用分治思想)
3.1、实战
有一台服务器,32G内存的配置,如何在内存中对1T的数据进行排序?
思路:
- 先对原始数据进行抽样
- 然后进行切片,划分界限,把数据分为32份
- 每一份都是32G的数据(32*32=1024G)
- 这样针对每一份数据在内存中排序,排序完之后,把32份数据有序的合并