大数据之Spark(二)--- RDD,RDD变换,RDD的Action,解决spark的数据倾斜问题,spark集成hadoop的HA

一、Spark集群运行
-------------------------------------------------------
    1.local          //本地模式
    2.standalone   //独立模式
    3.yarn       //yarn模式
    4.mesos          //mesos


二、解决maven打包,不包含scala类
-----------------------------------------------------
    1.setting --> compiler --> auto compiler 打钩

    2.在maven中添加打包scala插件
       
 <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>

            <groupId>com.it18zhang</groupId>
            <artifactId>SparkDemo1</artifactId>
            <version>1.0-SNAPSHOT</version>

            <build>
                <sourceDirectory>src/main/java</sourceDirectory>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <configuration>
                            <source>1.8</source>
                            <target>1.8</target>
                        </configuration>
                    </plugin>
                    <plugin>
                        <groupId>net.alchim31.maven</groupId>
                        <artifactId>scala-maven-plugin</artifactId>
                        <version>3.2.2</version>
                        <configuration>
                            <recompileMode>incremental</recompileMode>
                        </configuration>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>compile</goal>
                                    <goal>testCompile</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>

            <dependencies>
                <dependency>
                    <groupId>org.apache.spark</groupId>
                    <artifactId>spark-core_2.11</artifactId>
                    <version>2.1.0</version>
                </dependency>
            </dependencies>
        </project>

三、RDD 弹性分布式数据集
---------------------------------------------------------
    是spark的基本数据结构,是不可变数据集。RDD中的数据集进行逻辑分区,每个分区可以单独在集群节点
    进行计算。可以包含任何java,scala,python和自定义类型。

    RDD是只读的记录分区集合。RDD具有容错机制。

    创建RDD方式,一、并行化一个现有集合。

    hadoop 花费90%时间用户rw。、

    内存处理计算。在job间进行数据共享。内存的IO速率高于网络和disk的10 ~ 100之间。

    内部包含5个主要属性
    -----------------------
    1.分区列表
    2.针对每个split的计算函数。
    3.对其他rdd的依赖列表
    4.可选,如果是KeyValueRDD的话,可以带分区类。
    5.可选,首选块位置列表(hdfs block location);

    //默认并发度
    local.backend.defaultParallelism() = scheduler.conf.getInt("spark.default.parallelism", totalCores)
    taskScheduler.defaultParallelism = backend.defaultParallelism()
    sc.defaultParallelism =...; taskScheduler.defaultParallelism
    defaultMinPartitions = math.min(defaultParallelism, 2)
    sc.textFile(path,defaultMinPartitions)       //1,2


四、RDD变换
-------------------------------------------------------------
    1.对一个数据为{1,2, 3, 3} 的RDD进行基本的RDD转化操作
        a.map()
            //函数应用于RDD中的每个元素
            rdd.map(x=>x+1) => new RDD: {2,3,4,4}

        b.flatMap()
            //将压扁函数应用于RDD中的每个元素,通常用来切分单词
            rdd.flatMap(x=>x.to(3)) => new RDD: {1,2,3,2,3,3,3}

        c.filter()
            //返回一个通过传给filter()的函数的元素组成的RDD,如果不等于1就过滤掉
            rdd.filter(x=>x!=1) => new RDD: {2,3,3}

        d.distinct()
            //去除重复元素
            rdd.distinct() => new RDD: {1,2,3}

        e.sample(withReplacement,fraction,[seed])
            //对RDD进行采样,以及是否替换
            rdd.sample(false,0.5) => new RDD: { 非确定 }

        f.mapPartitions()
            //对每个分区进行应用变换,输入的Iterator,返回新的迭代器,可以对分区进行函数处理。
            //Iterator<T> => Iterator<U>

        g.mapPartitionsWithIndex(func)
            //同上,(Int, Iterator<T>) => Iterator<U>

        l.代码演示
            
import org.apache.spark.{SparkConf, SparkContext}

            import scala.collection.mutable.ArrayBuffer

            object Test1 {

                def main(args: Array[String]): Unit = {
                    val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
                    val sc: SparkContext = new SparkContext(sparkConf)

                    val data = List(1,2,3,3);
                    val rdd1 = sc.parallelize(data, 2)

                    //结果:{(1,1) (2,1) (3,1) (4,1)}
                    val rdd2 = rdd1.map( e => (e , 1))
                    rdd2.foreach(e => println(e))
                    println("-----------------------------")

                    //结果:{1,2,3,2,3,3,3}
                    val rdd3 = rdd1.flatMap( x => x.to(3) )
                    rdd3.foreach(println)
                    println("-----------------------------")

                    //结果:new RDD: {2,3,3}
                    val rdd4 = rdd1.filter( x => x != 1)
                    rdd4.foreach(println)
                    println("-----------------------------")

                    //结果:new RDD: {1,2,3}
                    val rdd5 = rdd1.distinct();
                    rdd5.foreach(println)
                    println("-----------------------------")

                    //结果:不确定
                    val rdd6 = rdd1.sample(false,0.5);
                    rdd6.foreach(println)
                    println("-----------------------------")

                    //结果:
                    // _1 _2 _3 _3
                    //
                    val rdd7 = rdd1.mapPartitions(
                        (it) => {
                            val buf = ArrayBuffer[String]();
                            for(e <- it)
                            {
                                buf.+=("_" + e)
                            }
                            buf.iterator;
                        }
                    )
                    rdd7.foreach(println)
                    println("-----------------------------")

                    //结果:
                    // 开始第0号分区
                    //_1
                    //_2
                    //开始第1号分区
                    //_3
                    //_3

                    val rdd8 = rdd1.mapPartitionsWithIndex(
                        (i,it) => {
                            val buf = ArrayBuffer[String]();
                            println("开始第" + i + "号分区")
                            for(e <- it)
                            {
                                buf.+=("_" + e)
                            }
                            buf.iterator;
                        }
                    )
                    rdd8.foreach(println)
                    println("-----------------------------")

                }

            }


    2.对数据分别为{1,2,3}和{3,4,5}的RDD进行针对两个RDD的转化操作
        a.union()
            //生成一个包含两个RDD中所有元素的RDD
            rdd.union(other) => new RDD:{1,2,3,3,4,5}

        b.intersection()
            //求两个RDD共同的元素RDD
            rdd.intersection(other) => new RDD: {3}

        c.subtract()
            //移除一个元素的内容
            rdd.subtract(other) => new RDD: {1,2}

        d.cartesian()
            //与另一个RDD的笛卡儿积
            rdd.cartesian(other) => new RDD: {(1,3),(1,4)...(3,4),(3,5)}

        e.代码演示
            
import org.apache.spark.{SparkConf, SparkContext}

            object Test2 {
                def main(args: Array[String]): Unit = {
                    val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
                    val sc: SparkContext = new SparkContext(sparkConf)

                    val data1 = List(1,2,3);
                    val data2 = List(3,4,5);
                    val rdd1 = sc.parallelize(data1, 2)
                    val rdd2 = sc.parallelize(data2, 2)

                    //结果:{1,2,3,3,4,5}
                    val rdd3 = rdd1.union(rdd2);
                    rdd3.foreach(println)
                    println("=========================================")

                    //结果:{3}
                    val rdd4 = rdd1.intersection(rdd2);
                    rdd4.foreach(println)
                    println("=========================================")

                    //结果:{1,2}
                    val rdd5 = rdd1.subtract(rdd2);
                    rdd5.foreach(println)
                    println("=========================================")

                    //结果:{(1,3) : (1,4) : (1,5): (2,3) : (3,3) : (2,4) : (2,5) : (3,4) : (3,5)}
                    val rdd6 = rdd1.cartesian(rdd2);
                    rdd6.foreach(e => print(e + " : "))
                    println("=========================================")
                }
            }


    3.单个pair RDD的转化操作(以键值对集合{(1,2),(3,4),(3,6)}为例)
        a.reduceByKey(func)
            //合并具有相同键的值,对相同键的对偶应用函数法则
            rdd.reduceByKey((x,y)=>x+y) => {(1,2),(3,10)}

        b.groupByKey()
            //对具有相同键的值进行分组
            (K,V) => (K,Iterable<V>)
            rdd.groupByKey() => {(1,[2]),(3,[4,6])}

        c.combineByKey(createCombiner,mergeCombiners,partitioner)
            //使用不同的返回类型合并具有相同键的值
              def combineByKey[C](
                  createCombiner: V => C,
                  mergeValue: (C, V) => C,
                  mergeCombiners: (C, C) => C): RDD[(K, C)] = self.withScope {
                combineByKeyWithClassTag(createCombiner, mergeValue, mergeCombiners)(null)
              }
            如下解释下3个重要的函数参数:
            createCombiner: V => C ,这个函数把当前的值作为参数,此时我们可以对其做些附加操作(类型转换)并把它返回 (这一步类似于初始化操作)
            mergeValue: (C, V) => C,该函数把元素V合并到之前的元素C(createCombiner)上 (这个操作在每个分区内进行)
            mergeCombiners: (C, C) => C,该函数把2个元素C合并 (这个操作在不同分区间进行)
            //结果:
            // (Wilma,(3,286.0))
            //(Fred,(3,274.0))
            val initialScores = Array(("Fred", 88.0), ("Fred", 95.0), ("Fred", 91.0), ("Wilma", 93.0), ("Wilma", 95.0), ("Wilma", 98.0))
            val d1 = sc.parallelize(initialScores)
            type MVType = (Int, Double) //定义一个元组类型(科目计数器,分数)
            d1.combineByKey(
                score => (1, score),                                            //createCombiner,将88变成(1,88)
                (c1: MVType, newScore) => (c1._1 + 1, c1._2 + newScore),        //mergeValue,针对每个分区:将相同key的分数聚合成新的tuple (2,88+91)
                (c1: MVType, c2: MVType) => (c1._1 + c2._1, c1._2 + c2._2)      //mergeCombiners,针对所有分区,将不同分区相同key的结果进行聚合
            ).foreach(println)
            println("=========================================")

        d.mapValues(func)
            //对pairRDD中的每个值value应用一个函数而不改变键
            rdd.mapValues(x=>x+1) => {(1,3),(3,5),(3,7)}

        e.flatMapValues()
            //对pairRDD中的每个值value应用一个返回迭代器的函数,然后返回的每个元素都生成一个对应原键的键值对记录,通常用于符号化和value的范围限定等
            //键有1和3,那么,就(1,2),(1,3),(1,4),(1,5) | (3,4),(3,5),因为(3,6),6不属于3 to 5范围,弃掉
            rdd.flatMapValues(x=>(x to 5)) => {(1,2),(1,3),(1,4),(1,5),(3,4),(3,5)}

        f.keys
            //返回一个仅包含键的RDD
            rdd.keys => {1,3,3}

        g.values()
            //返回一个仅包含值的RDD
            rdd.values() => {2,4,6}

        h.sortByKey()
            //返回一个根据键排序的RDD
            rdd.sortByKey() => {(1,2),(3,4),(3,6)}

        i.aggregateByKey(zeroValue)(seqOp, combOp, [numTasks])
            //def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U,combOp: (U, U) => U): RDD[(K, U)]
            //按照key进行聚合,把相同的Key按照seqOp函数进行捏合
            //对PairRDD中相同的Key值进行聚合操作,在聚合过程中同样使用了一个中立的初始值。和aggregate函数类似,
            //aggregateByKey返回值的类型不需要和RDD中value的类型一致。
            //因为aggregateByKey是对相同Key中的值进行聚合操作,所以aggregateByKey'函数最终返回的类型还是PairRDD,
            //对应的结果是Key和聚合后的值,而aggregate函数直接返回的是非RDD的结果。
            //步骤 List((1,3),(1,2),(1,4),(2,3))
            //1.输入<key,value>,以及初始值zeroValue("100").
            //2.每个分区:将分配到该分区部分List的第一个<k,v>的Value值,执行seqOp函数,得到与zeroValue类型相同的<k,new value>
            //3.继续该分区的下一个<k,v>,如果k和2步骤的结果k相同,就继续将2的结果作为zeroVlue进行聚合,得到新的<k,new value1>,
            //4.一直聚合下去,直到该分区所有相同key的value都已经按照seqOp函数处理完毕,那么分区聚合完毕。
            //5.然后对各个分区的结果执行combOp函数,将相同key的value继续聚合.
            //6.分区也聚合完毕,最终输出 <K,U>

            
import org.apache.spark.{SparkConf, SparkContext}

            /**
              * 将数据拆分成两个分区
              * //分区一数据
              * (1,3)
              * (1,2)
              * //分区二数据
              * (1,4)
              * (2,3)
              *
              * //分区一相同key的数据进行合并
              * seq: 100     3   //(1,3)开始和中立值进行合并  合并结果为 1003
              * seq: 1003     2   //(1,2)再次合并 结果为 10032
              *
              * //分区二相同key的数据进行合并
              * seq: 100     4  //(1,4) 开始和中立值进行合并 1004
              * seq: 100     3  //(2,3) 开始和中立值进行合并 1003
              *
              * 将两个分区的结果进行合并
              * //key为2的,只在一个分区存在,不需要合并 (2,1003)
              * (2,1003)
              *
              * //key为1的, 在两个分区存在,并且数据类型一致,合并
              * comb: 10032     1004
              * (1,100321004)
              */
            object TestApi_1 {
                def main(args: Array[String]): Unit = {

                    val sparkConf: SparkConf = new SparkConf().setAppName("AggregateByKey").setMaster("local")
                    val sc: SparkContext = new SparkContext(sparkConf)

                    val data=List((1,3),(1,2),(1,4),(2,3))
                    val rdd=sc.parallelize(data, 2)

                    //合并不同partition中的值,a,b得数据类型为zeroValue的数据类型
                    def combOp(a:String,b:String):String={
                        println("combOp: "+a+"\t"+b)
                        a+b
                    }
                    //合并在同一个partition中的值,a的数据类型为zeroValue的数据类型,b的数据类型为原value的数据类型
                    def seqOp(a:String,b:Int):String={
                        println("SeqOp:"+a+"\t"+b)
                        a+b
                    }
                    rdd.foreach(println)
                    //zeroValue:中立值,定义返回value的类型,并参与运算
                    //seqOp:用来在同一个partition中合并值
                    //combOp:用来在不同partiton中合并值
                    val aggregateByKeyRDD=rdd.aggregateByKey("100")(seqOp, combOp)

                    aggregateByKeyRDD.foreach(e => println(e))

                    //输出结果
                    //(2,1003)  (1,100321004)


                    sc.stop()
                }
            }

        j.代码演示
            
import org.apache.spark.{SparkConf, SparkContext}

            object Test3 {
                def main(args: Array[String]): Unit = {
                    val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
                    val sc: SparkContext = new SparkContext(sparkConf)

                    val data1 = List((1,2),(3,4),(3,6));
                    val rdd1 = sc.parallelize(data1, 2)

                    //1.结果:{(1,2),(3,10)}
                    val rdd2 = rdd1.reduceByKey((x,y) => x + y);
                    rdd2.foreach(e => print(e + ","))
                    println("=========================================")

                    //2.结果:{(1,CompactBuffer(2)),(3,CompactBuffer(4, 6))}
                    val rdd3 = rdd1.groupByKey();
                    rdd3.foreach(e => print(e + ","))
                    println("=========================================")

                    //3.结果
                    // (Wilma,(3,286.0))
                    //(Fred,(3,274.0))
                    val initialScores = Array(("Fred", 88.0), ("Fred", 95.0), ("Fred", 91.0), ("Wilma", 93.0), ("Wilma", 95.0), ("Wilma", 98.0))
                    val d1 = sc.parallelize(initialScores)
                    type MVType = (Int, Double) //定义一个元组类型(科目计数器,分数)
                    d1.combineByKey(
                        score => (1, score),                                            //createCombiner,将88变成(1,88)
                        (c1: MVType, newScore) => (c1._1 + 1, c1._2 + newScore),        //mergeValue,针对每个分区:将相同key的分数聚合成新的tuple (2,88+91)
                        (c1: MVType, c2: MVType) => (c1._1 + c2._1, c1._2 + c2._2)      //mergeCombiners,针对所有分区,将不同分区相同key的结果进行聚合
                    ).foreach(println)
                    println("=========================================")

                    //4.结果:{(1,3),(3,5),(3,7)}
                    val rdd4 = rdd1.mapValues(x => x + 1);
                    rdd4.foreach(e => print(e + ","))
                    println("=========================================")

                    //5.结果:{(1,2),(1,3),(1,4),(1,5),(3,4),(3,5)}
                    val rdd5 = rdd1.flatMapValues(x => x to 5);
                    rdd5.foreach(e => print(e + ","))
                    println("=========================================")

                    //6.结果:{1,3,3}
                    val rdd6 = rdd1.keys
                    rdd6.foreach(e => print(e + ","))
                    println("=========================================")

                    //7.结果:{2,4,6}
                    val rdd7 = rdd1.values
                    rdd7.foreach(e => print(e + ","))
                    println("=========================================")

                    //8.结果:{(1,2),(3,4),(3,6)}
                    val rdd8 = rdd1.sortByKey()
                    rdd8.foreach(e => print(e + ","))
                    println("=========================================")

                    //9.结果:(2,1003)  (1,100321004)
                    val data=List((1,3),(1,2),(1,4),(2,3))
                    val rdd=sc.parallelize(data, 2)

                    //合并不同partition中的值,a,b得数据类型为zeroValue的数据类型
                    def combOp(a:String,b:String):String={
                        println("combOp: "+a+"\t"+b)
                        a+b
                    }
                    //合并在同一个partition中的值,a的数据类型为zeroValue的数据类型,b的数据类型为原value的数据类型
                    def seqOp(a:String,b:Int):String={
                        println("SeqOp:"+a+"\t"+b)
                        a+b
                    }
                    rdd.foreach(println)
                    //zeroValue:中立值,定义返回value的类型,并参与运算
                    //seqOp:用来在同一个partition中合并值
                    //combOp:用来在不同partiton中合并值
                    val aggregateByKeyRDD=rdd.aggregateByKey("100")(seqOp, combOp)

                    aggregateByKeyRDD.foreach(e => println(e))
                    println("=========================================")
                }
            }

    4.针对两个pairRDD的转换操作(rdd={(1,2),(3,4),(3,6)} other={(3,9)})
        a.subtractByKey
            //删掉RDD中的键与otherRDD中的键相同的元素
            rdd.subtractByKey(other) => new RDD: {(1,2)}

        b.join
            //内链接:连接操作,对两个rdd进行内链接
            rdd.join(other) => new RDD: {(3,(Some(4),9)),(3,(Some(6),9))}

        c.rightOuterJoin
            //右外连接:连接操作,确保右侧rdd的键必须存在
            rdd.rightOuterJoin(other) => new RDD: {(3,(4,Some(9))),(3,(4,Some(9)))}

        d.leftOutrtJoin
            //左外连接:连接操作,确保左侧rdd的键必须存在
            rdd.leftOuterJoin(other) => new RDD: {(1,(2,None)),(3,(4,Some(9))),(3,(6,Some(9)))}

        e.cogroup
            //将两个rdd中拥有相同键的数据分组到一起
            rdd.cogroup(other) => new RDD: {(1,([2],[])),(3,([4,6],[9]))}

        f.代码演示
            
import org.apache.spark.{SparkConf, SparkContext}

            object TestJoin {

                def main(args: Array[String]): Unit = {
                    val sparkConf: SparkConf = new SparkConf().setAppName("AggregateByKey").setMaster("local")
                    val sc: SparkContext = new SparkContext(sparkConf)

                    val nameData = List((1,"tom1"),(2,"tom2"),(3,"tom3"),(4,"tom4"),(5,"tom5"))
                    val scoreData = List((1,600),(2,580),(3,450),(4,600))
                    val nameRdd = sc.parallelize(nameData, 2)
                    val scoreRdd = sc.parallelize(scoreData, 2)

                    /*
                     结果只包含两个rdd都有的元素,输出都非空
                     (4,(tom4,600))
                     (2,(tom2,580))
                     (1,(tom1,600))
                     (3,(tom3,450))
                    */
                    val r = nameRdd.join(scoreRdd);
                    r.foreach(e => println(e));

                    /*
                     结果:保证左侧必须全部有
                     (1,(tom1,Some(600)))
                     (3,(tom3,Some(450)))
                     (5,(tom5,None))
                     (2,(tom2,Some(580)))
                     (4,(tom4,Some(600)))
                    */
                    val r1 = nameRdd.leftOuterJoin(scoreRdd);
                    r1.foreach(e => println(e));

                    /*
                     结果:保证右侧必须全部有值
                    (1,(Some(tom1),600))
                    (3,(Some(tom3),450))
                    (4,(Some(tom4),600))
                    (2,(Some(tom2),580))
                    */
                    val r2 = nameRdd.rightOuterJoin(scoreRdd);
                    r2.foreach(e => println(e));


                    /*
                    (4,(CompactBuffer(tom4),CompactBuffer(600)))
                    (2,(CompactBuffer(tom2),CompactBuffer(580)))
                    (1,(CompactBuffer(tom1),CompactBuffer(600)))
                    (3,(CompactBuffer(tom3),CompactBuffer(450)))
                    (5,(CompactBuffer(tom5),CompactBuffer()))
                    */
                    val r3 = nameRdd.cogroup(scoreRdd);
                    r3.foreach(e => println(e));

                    println("-------------------------------------------------")

                    /*
                    (5,tom5)
                    */
                    val r4 = nameRdd.subtractByKey(scoreRdd);
                    r4.foreach(e => println(e));
                }
            }


五、其他RDD变换
------------------------------------------------------
    1.pipe
        //将rdd的元素传递给脚本或者命令cmd/shell,执行结果返回形成新的RDD
        val rdd9 = sc.parallelize(Array("D:\\calllog")).pipe("dir")
        rdd9.foreach(println)
        println("-----------------------------")

    2.coalesce(numPartitions)
        //减少分区数直到numPartitions

    3.repartition
        //再分区 -- 可增可减

    4.repartitionAndSortWithinPartitions(partitioner)
        //再分区并在分区内进行排序

    5.代码演示
        
import org.apache.spark.{SparkConf, SparkContext}

        object Test6 {

            def main(args: Array[String]): Unit = {
                val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
                val sc: SparkContext = new SparkContext(sparkConf)

                val data = List(5,5,7,7,1,67,122,34,1,2,3,3);
                val rdd1 = sc.parallelize(data, 4)
                //"rdd1 parts:4"
                println("rdd1 parts:" + rdd1.partitions.length)

                val rdd2 = rdd1.coalesce(3);
                //"rdd2 parts:3"
                println("rdd2 parts:" + rdd2.partitions.length)

                val rdd3 = rdd1.coalesce(5);
                //"rdd3 parts:4",coalesce只能减少分区,不能增加分区
                println("rdd3 parts:" + rdd3.partitions.length)

                //需要增加分区,使用再分区
                val rdd4 = rdd1.repartition(5);
                //"rdd4 parts:5",coalesce只能减少分区,不能增加分区,repartition可以增加分区也可以减少分区
                println("rdd4 parts:" + rdd4.partitions.length)
            }

        }


五、RDD的Action行动
--------------------------------------------------------
    1.针对单个元素RDD的行动操作,以{1,2,3,3}为例 -- spark是懒操作,只有触发行动,才会进行行动之前的所有计算过程
        a.collect()
            //展示所有元素
            rdd.collect() => new Add: {1,2,3,3}

        b.count()
            //统计元素个数
            rdd.count() => Int:4

        c.countByValue()
            //统计各元素在rdd中,出现的次数,返回一个Map[T,Long]的映射
            rdd.countByValue() => Map[T,Long]: {(1,1),(2,1),(3,2)}

        d.take(num)
            //take用于获取RDD中从0到num-1下标的元素,不排序
            //def take(num: Int): Array[T]
            rdd.take(2) => Array[T]: {1,2}

        e.top(num)
            //top函数用于从RDD中,按照默认(降序)或者指定的排序规则,返回前num个元素。
            //def top(num: Int)(implicit ord: Ordering[T]): Array[T]
            rdd.top(2) => Array[T]: {3,3}

            //如果top之前定义了隐式排序法则,那么就会先按照法则进行排序,然后取出前num个元素
             implicit val myOrd = implicitly[Ordering[Int]].reverse
             rdd.top(2) => Array[T]: {1,2}

        f.takeOrdered(num)(ordering)
            //按提供的顺序,返回最前面的num个元素的集合,缺省表示升序
            rdd.takeOrdered(2)(myOrdering) => Array[T]: {3,3}

            //如果takeOrdered之前定义了隐式排序法则ordering,那么就会先按照法则进行排序,然后返回最前面的num个元素的集合
            implicit val myOrd = implicitly[Ordering[Int]].reverse
            rdd.takeOrdered(2)(myOrdering) => Array[T]: {1,2}

        g.takeSample(withReplacement,num,[seed])
            //从rdd中返回任意一些元素
            rdd.takeSample(false,1) => Array[T] : { 非确定的 }

        h.reduce(func)
            //按照函数法则,整合RDD中的所有数据,循环应用法则
            //def reduce(f: (T, T) => T): T
            rdd.reduce((x,y)=>x+y) => T: 9      //(((1 + 2) + 3) + 3)

        i.fold(zero)(func)
            //和reduce一样,但是需要初始值
            rdd.fold(1)((x,y)=>x+y) => T: 13     //注意,zero作为op函数的初始值,是每个分区的初始值。注意:是每个分区初始值是1,然后4个分区,结果就是13

        j.aggregate(zeroValue)(seqOp,combOp)
            //和reduce()相似,但是通常返回不同类型的函数
            //将每个分区里面的元素进行聚合,然后用combine函数将每个分区的结果和初始值(zeroValue)进行combine操作。
            //这个函数最终返回的类型不需要和RDD中元素类型一致。
            //seqOp操作会聚合各分区中的元素,然后combOp操作把所有分区的聚合结果再次聚合,两个操作的初始值都是zeroValue.  
            //seqOp的操作是遍历分区中的所有元素(T),第一个T跟zeroValue做操作,结果再作为与第二个T做操作的zeroValue,直到遍历完整个分区。
            //combOp操作是把各分区聚合的结果,再聚合。
            //aggregate函数返回一个跟RDD不同类型的值。
            //因此,需要一个操作seqOp来把分区中的元素T合并成一个U,另外一个操作combOp把所有U聚合。

            scala> rdd.par.aggregate((0,0))(
            (acc,number) => (acc._1 + number, acc._2 + 1),
            (par1,par2) => (par1._1 + par2._1, par1._2 + par2._2)
            )
            res0: (Int, Int) = (45,9)

            scala> res0._1 / res0._2
            res1: Int = 5

            过程大概这样:
            首先,初始值是(0,0),这个值在后面2步会用到。
            然后,(acc,number) => (acc._1 + number, acc._2 + 1),number即是函数定义中的T,这里即是List中的元素。
            所以acc._1 + number,acc._2 + 1的过程如下。

            1.  0+1,  0+1
            2.  1+2,  1+1
            3.  3+3,  2+1
            4.  6+4,  3+1
            5.  10+5,  4+1
            6.  15+6,  5+1
            7.  21+7,  6+1
            8.  28+8,  7+1
            9.  36+9,  8+1
            结果即是(45,9)
            这里演示的是单线程计算过程,实际Spark执行中是分布式计算,可能会把List分成多个分区,假如3个,p1(1,2,3,4),p2(5,6,7,8),p3(9),
            经过计算各分区的的结果(10,4),(26,4),(9,1),这样,执行(par1,par2) =>(par1._1 + par2._1, par1._2 + par2._2)就是(10+26+9,4+4+1)
            即(45,9),
            再求平均值就简单了。

        k.foreach(func)
            //遍历,对RDd中的每个元素使用给定的元素
            rdd.foreach(func) => 无

        l.first
            //取出第一个元素take(1)

        m.代码演示
            
import org.apache.spark.{SparkConf, SparkContext}

            object Test4 {
                def main(args: Array[String]): Unit = {
                    val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
                    val sc: SparkContext = new SparkContext(sparkConf)

                    val data1 = List(1,2,3,3);
                    val rdd1 = sc.parallelize(data1, 2)

                    //1.结果:{1,2,3,3}
                    val rdd2 = rdd1.collect();
                    rdd2.foreach(e => print(e + ","))
                    println("=========================================")

                    //2.结果:4
                    val rdd3 = rdd1.count();
                    println(rdd3)
                    println("=========================================")

                    //3.结果:{(2,1),(1,1),(3,2)}
                    val rdd4 = rdd1.countByValue();
                    rdd4.foreach(e => print(e + ","))
                    println("=========================================")

                    //4.结果:{1,2}
                    val rdd5 = rdd1.take(2);
                    rdd5.foreach(e => print(e + ","))
                    println("=========================================")

                    //5.结果:{3,3}
                    val rdd6 = rdd1.top(2);
                    //自定义排序函数
                    //implicit val myOrd = implicitly[Ordering[Int]].reverse
                    rdd6.foreach(e => print(e + ","))
                    println("=========================================")

                    //6.结果:本来该是{1,2},但是自定义了反转函数,所以变成了{3,3}
                    //自定义排序函数
                    implicit val myOrd = implicitly[Ordering[Int]].reverse
                    val rdd7 = rdd1.takeOrdered(2);
                    rdd7.foreach(e => print(e + ","))
                    println("=========================================")

                    //7.结果:{1,3,3},取三个元素,但是不确定是哪3个
                    val rdd8 = rdd1.takeSample(false,3);
                    rdd8.foreach(e => print(e + ","))
                    println("=========================================")

                    //8.结果:9
                    val rdd9 = rdd1.reduce((x,y)=>x+y);
                    println(rdd9)
                    println("=========================================")

                    //9.结果:12,与分区数有关系
                    val rdd10 = rdd1.fold(1)((x,y)=>x+y);
                    println(rdd10)
                    println("=========================================")


                    //10.结果:(45,9)
                    val data = List(1,2,3,4,5,6,7,8,9);
                    val rdd11 = sc.parallelize(data, 2)
                    val rdd12 = rdd11.aggregate((0,0))(
                        (acc,number) => (acc._1 + number, acc._2 + 1),
                        (par1,par2) => (par1._1 + par2._1, par1._2 + par2._2)
                    );
                    println(rdd12)
                    println("=========================================")
                }
            }

    2.针对PairRDD 的行动操作,以键值对集合{(1,2),(3,4),(3,6)} 为例
        a.countByKey()
            对每个键对应的元素分别计数
            rdd.countByKey() => {(1,1),(3,2)}

        b.collectAsMap()
            //将结果以映射表的形式返回,以便查询
            rdd.collectAsMap() => Map : {(1,2),(3,4),(3,6)}

        c.lookup(key)
            返回给定键对应的所有值
            rdd.lookup(3) => [4,6]

        d.代码演示
            
import org.apache.spark.{SparkConf, SparkContext}

            object Test5 {
                def main(args: Array[String]): Unit = {
                    val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
                    val sc: SparkContext = new SparkContext(sparkConf)

                    val data1 = List((1,2),(3,4),(3,6));
                    val rdd1 = sc.parallelize(data1, 2)

                    //1.结果:{(1,1),(3,2)}
                    val rdd2 = rdd1.countByKey();
                    rdd2.foreach(e => print(e + ","))
                    println("=========================================")

                    //2.结果:
                    // 1:2'
                    // 3:4
                    // 3:6
                    val rdd3:scala.collection.Map[Int,Int] = rdd1.collectAsMap();
                    rdd3.foreach(
                        u =>{
                            println((u _1).toString() + ":" + u._2);
                        }
                    )
                    println("=========================================")

                    //3.结果:{4,6}
                    val rdd4 = rdd1.lookup(3);
                    rdd4.foreach(e => print(e + ","))
                    println("=========================================")
                }
            }


六、其他RDD Action
------------------------------------------------------
    1.saveAsTextFile(path)
        //保存到文件[aa是一个文件夹,用于保存MR运行的结果]
        //rdd4.saveAsTextFile("d:\\aa")

    2.saveAsSequenceFile(path)
        //保存成序列文件

    3.saveAsObjectFile(path)
        //(Java and Scala)
        //reduceKey的过程中指定分区数
        val rdd4 = rdd3.reduceByKey(_ + _,3)
        rdd4.saveAsObjectFile("d:\\aa")


七、解决spark的数据倾斜问题
-----------------------------------------------------------
   
import org.apache.spark.{SparkConf, SparkContext}

   import scala.util.Random

   /**
     * spark数据倾斜
     */
   object Test7 {

       def main(args: Array[String]): Unit = {
           val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
           val sc: SparkContext = new SparkContext(sparkConf)

           val rdd1 = sc.textFile("d:\\1.txt")
           val rdd2 = rdd1.flatMap(line => line.split(" "))
           val rdd3 = rdd2.map(word => (word,1))

           //对key进行改造,增加hash分区后缀
           val rdd4 = rdd3.map(t => {
               val word = t._1;
               val r = Random.nextInt(10);
               (word + "_" + r.toString , t._2)
           });
           //首次聚合
           val rdd5 = rdd4.reduceByKey((x,y) => x + y,10);

           //第二次映射,去掉“_num”hash分区数
           val rdd6 = rdd5.map(
               t =>{
                   val word = t._1.split("_")(0);
                   (word,t._2)
               }
           )
           //第二次聚合
           val rdd7 = rdd6.reduceByKey(_ + _,10);
           //打印结果
           rdd7.foreach(println)
       }
   }


八、spark集成hadoop的HA
-------------------------------------------------------
    1.复制core-site.xml + hdfs-site.xml到spark/conf目录下
    2.分发文件到spark所有work节点
    3.启动spark集群
    4.启动spark-shell,连接spark集群上
        $>spark-shell --master spark://s100:7077
        $scala>sc.textFile("hdfs://mycluster/data/spark/1.txt").collect();






















猜你喜欢

转载自blog.csdn.net/xcvbxv01/article/details/83753474