【Spark】SparkRDD-JavaAPI开发手册(函数式编程)

文章目录

  • Github
  • Note
  • 遇到的大坑!
  • 装备
  • Core
  • 总结

Github

地址:https://github.com/ithuhui/hui-base-java
模块:【hui-base-spark】
分支:master
位置:com.hui.base.spark.rdd

Note

老实说,相比那些抄袭来抄袭去的blog,这篇RDD的JavaAPI…我是每个常用API都结合实例解释并且用了Java函数式编程写的…我这懒人够良心了…

如果能帮到你们的话,我很开心。

不需要抄博客,博客不是为了点击量,是为了总结自己,转载标明一下作者和链接,谢谢~

  • wiki解释

Spark RDD(英语:Resilient Distributed Dataset,弹性分布式数据集)是一种数据存储集合。只能由它支持的数据源或是由其他RDD经过一定的转换(Transformation)来产生。在RDD上可以执行的操作有两种转换(Transformation)和行动(Action),每个 RDD 都记录了自己是如何由持久化存储中的源数据计算得出的,即其血统(Lineage)

  • 重点
  1. RDD是一个分布式集合
  2. SparkRDD特性惰性行为,只有激活Action操作才会真正开始计算
  3. 两种转换操作Transformation(惰性,只代表逻辑)和行动Action(起床,开始执行)
  4. 血统(Lineage)怎么理解呢?血统具有继承的特性,能追根溯源。Lineage记录了你的Transformation和Action,通过 Lineage 获取足够的信息来重新运算和恢复丢失的数据分区,这也是它的容错原理,某节点挂掉,再通过Lineage记录重新执行一次操作即可
  • 建议
  1. 函数式编程了解下
  2. 你是写java的,可以先Java入手,但是不能丢弃scala版本,spark的爸爸毕竟是scala… (scala也更容易)
  3. 不要demo拿去就运行,先看看这些简单操作的目的,代码注释我都写了
  • 常用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

装备

  1. maven依赖引入
  2. 切实的去试一下怎么玩
  3. 函数式编程了解一下

Core

挑一些稍微难理解的RDD方法,没有展示的可以看我的github

分析前提注意

因为太懒了,用的是我写迪杰斯特拉最短距离算法生成的结果数据文件,对这个有兴趣的看:https://blog.csdn.net/HuHui_/article/details/83020917

数据格式:进站站点名字,出站站点名字,最短距离经历站点数,最短距离

举例: 广州南站,广州南站,0,0

  • init
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());
        }
    }
  • Transformation RDD:
  1. flatMap
/**
     * 元素转换. 参数->数组参数
     * 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());
    }
  1. mapPartitions
 /**
     * 元素转换,在每一个分区内部进行元素转换.
     * 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();
    }
  1. mapPartitionsWithIndex
/**
     * 元素转换,在每一个分区内部进行元素转换.
     * 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();
    }
  1. join
/**
     * 集合关联. 合并相同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());
    }
  1. coGroup
 /**
     * 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());
    }
  • Action RDD:
  1. reduce
/**
     * 聚合(整合数据).
     *
     * @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);
    }

总结

  1. RDD实操几次,个人感觉都不难
  2. API不难,但是要考虑分区,内存,性能才是重点
  3. 应用场景一般在数据分析,然后分析过程中保存结果集

你可能感兴趣的:(BigData)