地址:https://github.com/ithuhui/hui-base-java
模块:【hui-base-spark】
分支:master
位置:com.hui.base.spark.rdd
老实说,相比那些抄袭来抄袭去的blog,这篇RDD的JavaAPI…我是每个常用API都结合实例解释并且用了Java函数式编程写的…我这懒人够良心了…
如果能帮到你们的话,我很开心。
不需要抄博客,博客不是为了点击量,是为了总结自己,转载标明一下作者和链接,谢谢~
Spark RDD(英语:Resilient Distributed Dataset,弹性分布式数据集)是一种数据存储集合。只能由它支持的数据源或是由其他RDD经过一定的转换(Transformation)来产生。在RDD上可以执行的操作有两种转换(Transformation)和行动(Action),每个 RDD 都记录了自己是如何由持久化存储中的源数据计算得出的,即其血统(Lineage)
常用RDD(所有方法我都写了DEMO)
Transformation行为:
方法 | 官方解释 | Hui解释 |
---|---|---|
map(func) | 返回一个新的分布式数据集,该数据集由每一个输入元素经过func函数转换后组成 | 元素转换 . 参数->你希望要的参数 |
fitler(func) | 返回一个新的数据集,该数据集由经过func函数计算后返回值为true的输入元素组成 | 元素转换. 参数->数组参数 |
flatMap(func) | 类似于map,但是每一个输入元素可以被映射为0或多个输出元素(因此func返回一个序列,而不是单一元素) | 元素转换. 参数->pair参数 (key value) |
mapPartitions(func) | 类似于map,但独立地在RDD上每一个分片上运行,因此在类型为T的RDD上运行时,func函数类型必须是Iterator[T]=>Iterator[U] | 元素转换,在每一个分区内部进行元素转换. |
mapPartitionsWithIndex(func) | 类似于mapPartitons,但func带有一个整数参数表示分片的索引值。因此在类型为T的RDD上运行时,func函数类型必须是(Int,Iterator[T])=>Iterator[U] | 元素转换,在每一个分区内部进行元素转换. |
sample(withReplacement,fraction,seed) | 根据fraction指定的比例对数据进行采样,可以选择是否用随机数进行替换,seed用于随机数生成器种子 | 采样 |
union(otherDataSet) | 返回一个新数据集,新数据集是由原数据集和参数数据集联合而成 | 并集 |
distinct([numTasks]) | 返回一个包含原数据集中所有不重复元素的新数据集 | 去重 |
groupByKey([numTasks]) | 在一个(K,V)数据集上调用,返回一个(K,Seq[V])对的数据集。注意默认情况下,只有8个并行任务来操作,但是可以传入一个可选的numTasks参数来改变它 | 分组 |
reduceByKey(func,[numTasks]) | 在一个(K,V)对的数据集上调用,返回一个(K,V)对的数据集,使用指定的reduce函数,将相同的key的值聚合到一起。与groupByKey类似,reduceByKey任务的个数是可以通过第二个可选参数来设置的 | 聚合 |
sortByKey([[ascending],numTasks]) | 在一个(K,V)对的数据集上调用,K必须实现Ordered接口,返回一个按照Key进行排序的(K,V)对数据集。升序或降序由ascending布尔参数决定 | 排序 |
join(otherDataset0,[numTasks]) | 在类型为(K,V)和(K,W)数据集上调用,返回一个相同的key对应的所有元素在一起的(K,(V,W))数据集 | 集合关联. 合并相同key的value |
cogroup(otherDataset,[numTasks]) | 在类型为(K,V)和(K,W)数据集上调用,返回一个(K,Seq[V],Seq[W])元祖的数据集。这个操作也可以称为groupwith | 分组 |
cartesain(ohterDataset) | 笛卡尔积,在类型为T和U类型的数据集上调用,返回一个(T,U)对数据集(两两的元素对) | 笛卡尔积 |
Action行为:
方法 | Hui解释 | |
---|---|---|
reduce(func) | 通过函数func(接收两个参数,返回一个参数)聚集数据集中的所有元素。这个功能必须可交换且可关联的,从而可以正确的并行运行 | 聚合 |
collect() | 在驱动程序中,以数组形式返回数据集中的所有元素。通常在使用filter或者其他操作返回一个足够小的数据子集后再使用会比较有用 | 查询全部 |
count() | 返回数据集元素个数 | 数量 |
first() | 返回数据集第一个元素(类似于take(1)) | 第一个元素 |
take(n) | 返回一个由数据集前n个元素组成的数组 | 前面N个元素 |
sample(withReplacement,num,seed) | 注意 这个操作目前并非并行执行,而是由驱动程序计算所有的元素返回一个数组,该数组由从数据集中随机采样的num个元素组成,可以选择是否由随机数替换不足的部分,seed用户指定随机数生成器种子 | 采样 |
saveAsTextFile(path) | 将数据集的元素以textfile的形式保存到本地文件系统–HDFS或者任何其他Hadoop支持的文件系统。对于每个元素,Spark将会调用toString方法,将它转换为文件中的文本行 | 保存文件(TXT格式) |
saveAsSequenceFile(path) | 将数据集中的元素以Hadoop sequencefile的格式保存到指定的目录下,可以是本地系统、HDFS或者任何其他的Hadoop支持的文件系统。这个只限于由key-value对组成,并实现了Hadoop的Writable接口,或者可以隐式的转换为Writable的RDD(Spark包括了基本类型转换,例如Int、Double、String等) | 保存文件(Sequence格式) |
countByKey() | 对(K,V)类型的RDD有效,返回一个(K,Int)对的map,表示每一个key对应的元素个数 | 数key |
foreach(func) | 在数据集的每一个元素上,运行函数func进行更新。通常用于边缘效果,例如更新一个叠加器,或者和外部存储系统进行交互,如HBase | 循环 |
org.apache.spark.SparkException: Task not serializable
Caused by: java.io.NotSerializableException:
解决方案我另外写了一篇BLOG
https://blog.csdn.net/HuHui_/article/details/83905302
挑一些稍微难理解的RDD方法,没有展示的可以看我的github
分析前提注意:
因为太懒了,用的是我写迪杰斯特拉最短距离算法生成的结果数据文件,对这个有兴趣的看:https://blog.csdn.net/HuHui_/article/details/83020917
数据格式:进站站点名字,出站站点名字,最短距离经历站点数,最短距离
举例: 广州南站,广州南站,0,0
private transient SparkConf sparkConf;
private transient JavaSparkContext sparkContext;
private static final String FILE_PATH = TransformationRDDTest.class.getClassLoader().getResource("demo.txt")
.toString();
@Before
public void before() throws Exception {
sparkConf = new SparkConf().setMaster("local[4]").setAppName("test");
// sparkConf.set("spark.serializer", "org.apache.spark.serializer.JavaSerializer");
sparkContext = new JavaSparkContext(sparkConf);
}
/**
* 打印测试.
*
* @param collect the collect
* @since hui_project 1.0.0
*/
private void checkResult(List<?> collect) {
for (Object o : collect) {
System.out.println(o.toString());
}
}
/**
* 元素转换. 参数->数组参数
* demo计算目的:获取地铁站信息切分后 获取数组信息1.出发站 2.终点站 3.经历站点数 4.距离
* @since hui_project 1.0.0
*/
@Test
public void testFlatMap() {
JavaRDD<String> textRDD = sparkContext.textFile(FILE_PATH);
JavaRDD<String> splitRDD = textRDD
.flatMap(x -> Arrays.asList(x.split(",")).iterator());
checkResult(splitRDD.collect());
}
/**
* 元素转换,在每一个分区内部进行元素转换.
* demo计算目的:算平方。(单元测试比较难看出来分区作用),和Map主要区别是map针对每个元素,mapPartition是对整
* 个分区进行操作,但是会一次性把整个分区的数据load到内存,如果数据集大会有可能产生OOM,可以执行参数增加分区数量
* @since hui_project 1.0.0
*/
@Test
public void testMapPartitions() {
JavaRDD<Integer> parallelize = sparkContext.parallelize(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 3);
JavaRDD<Tuple2<Integer, Integer>> rdd = parallelize
.mapPartitions(x -> getSquare(x));
checkResult(rdd.collect());
}
/**
* Gets square.
*
* @param it the it
* @return the square
* @since hui_project 1.0.0
*/
@Transient
private Iterator<Tuple2<Integer, Integer>> getSquare(Iterator<Integer> it) {
ArrayList<Tuple2<Integer, Integer>> results = new ArrayList<>();
while (it.hasNext()) {
Integer next = it.next();
results.add(new Tuple2<>(next, next * next));
}
return results.iterator();
}
/**
* 元素转换,在每一个分区内部进行元素转换.
* demo计算目的:算平方。(参数1是分区的索引)
*
* @since hui_project 1.0.0
*/
@Test
public void testMapPartitionsWithIndex(){
JavaRDD<Integer> parallelize = sparkContext.parallelize(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 3);
JavaRDD<Tuple2<Integer, Integer>> rdd = parallelize.mapPartitionsWithIndex((x, y) -> getSquareWithIndex(x, y), false);
checkResult(rdd.collect());
}
/**
* Get square with index iterator.
*
* @param partIndex the part index
* @param it the it
* @return the iterator
* @since hui_project 1.0.0
*/
@Transient
public Iterator<Tuple2<Integer, Integer>> getSquareWithIndex(Integer partIndex, Iterator<Integer> it){
ArrayList<Tuple2<Integer, Integer>> results = new ArrayList<>();
while (it.hasNext()) {
Integer next = it.next();
results.add(new Tuple2<>(partIndex, next * next));
}
return results.iterator();
}
/**
* 集合关联. 合并相同key的value
* demo计算目的:今年和去年都获奖的同学,获奖项的科目都有哪些
* @since hui_project 1.0.0
*/
@Test
public void testJoin() {
//今年同学获奖的科目
JavaPairRDD<Object, Object> rdd1 = sparkContext.parallelize(Arrays.asList(
new Tuple2("xiaoming", "语文")
, new Tuple2("xiaoming", "数学")
, new Tuple2("lihua", "数学")
, new Tuple2("xiaofeng", "艺术")
, new Tuple2("test", "艺术")))
.mapToPair(x -> new Tuple2<>(x._1, x._2));
//去年同学获奖的科目
JavaPairRDD<Object, Object> rdd2 = sparkContext.parallelize(Arrays.asList(
new Tuple2("xiaoming", "艺术")
, new Tuple2("lihua", "艺术")
, new Tuple2("xiaofeng", "语文")))
.mapToPair(x -> new Tuple2<>(x._1, x._2));
JavaPairRDD<Object, Tuple2<Object, Object>> join = rdd1.join(rdd2);
checkResult(join.collect());
}
/**
* Test co group.
* demo计算目的: 以成绩分组 同学([成绩优秀学科],[成绩中等学科],[成绩差劲学科])
* @since hui_project 1.0.0
*/
@Test
public void testCoGroup() {
//成绩优秀的学生+科目
JavaRDD<Tuple2<String, String>> scoreDetails1 = sparkContext.parallelize(Arrays.asList(
new Tuple2("xiaoming", "语文")
, new Tuple2("xiaoming", "数学")
, new Tuple2("lihua", "数学")
, new Tuple2("xiaofeng", "艺术")));
//成绩中等的学生+科目
JavaRDD<Tuple2<String, String>> scoreDetails2 = sparkContext.parallelize(Arrays.asList(
new Tuple2("xiaoming", "艺术")
, new Tuple2("lihua", "艺术")
, new Tuple2("xiaofeng", "语文")));
//成绩差的学生+科目
JavaRDD<Tuple2<String, String>> scoreDetails3 = sparkContext.parallelize(Arrays.asList(
new Tuple2("xiaoming", "英语")
, new Tuple2("lihua", "英语")
, new Tuple2("lihua", "数学")
, new Tuple2("xiaofeng", "数学")
, new Tuple2("xiaofeng", "英语")));
JavaPairRDD<String, String> scoreMapRDD1 = JavaPairRDD.fromJavaRDD(scoreDetails1);
JavaPairRDD<String, String> scoreMapRDD2 = JavaPairRDD.fromJavaRDD(scoreDetails2);
JavaPairRDD<String, String> scoreMapRDD3 = JavaPairRDD.fromJavaRDD(scoreDetails3);
JavaPairRDD<String, Tuple3<Iterable<String>, Iterable<String>, Iterable<String>>> cogroupRDD =
scoreMapRDD1.cogroup(scoreMapRDD2, scoreMapRDD3);
checkResult(cogroupRDD.collect());
}
/**
* 聚合(整合数据).
*
* @since hui_project 1.0.0
*/
@Test
public void testReduce() {
JavaRDD<Integer> parallelize = sparkContext.parallelize(Arrays.asList(1, 2, 3, 4), 3);
Tuple2<Integer, Integer> reduce = parallelize.mapToPair(x -> new Tuple2<>(x, 1))
.reduce((x, y) -> getReduce(x, y));
System.out.println("数组sum:" + reduce._1 + " 计算次数:" + (reduce._2 - 1));
}
/**
* 计算逻辑.
* (x)总和->数组的每一个数相加总和
* (y)总和 ->计算次数
* @param x the x
* @param y the y
* @return the reduce
* @since hui_project 1.0.0
*/
@Transient
public Tuple2 getReduce(Tuple2<Integer, Integer> x, Tuple2<Integer, Integer> y) {
Integer a = x._1();
Integer b = x._2();
a += y._1();
b += y._2();
return new Tuple2(a, b);
}