大数据框架之Spark基础知识与认知-RDD

spark核心编程

  • 查缺:

    java中 .flush() 这个方法是清除缓存

    在传对象到另一个电脑的时候要序列化 只需要类继承Serializable即可

    Scala中 option表示防止空指针异常,这个数据可有可无

    项目中的相对路径是以该项目根目录文件为标准,即为文件总目录

    slices 切片 切片数量

  • 单词补漏

    1. socket: 插座,发送端,可以理解为需求的客户端,发送请求
    2. NotSerializableExcenption 序列化异常
    3. excutor 执行端 可以理解为服务器

io操作和rdd原理之间关系—>装饰者设计模式

  • io操作图解

以sparkrdd wordcount 案例为对比

什么是RDD

RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是 Spark 中最基本的数据处理模型。代码中是一个抽象类,它代表一个弹性的、不可变、可分区、里面的元素可并行计算的集合。
  • RDD操作图解

  •  

    RDD 数据处理方式类似于io流,也有装饰者设计模式

    RDD的数据只有在collect方法时,才会真正的执行逻辑操作

    RDD是不保存数据的,保存的是数据逻辑,而io保存部分数据

  • RDD核心属性

    1. 分区列

       

  • RDD创建

    • RDD内存创建,和分区规则,数值的去向

      1. ps: seq为需要计算的数据

        //    将内存中数据放入创建RDD对象框架中,这两个方法是一样的,makeRDD底层调用context.parallelize(seq)方法
        val value1:RDD[Int] = context.parallelize(seq)
        val value: RDD[Int] = context.makeRDD(seq)
        
      2. RDD内存创建分区规则,数值的去向

        // RDD的并行度 & 分区
        // makeRDD方法可以传递第二个参数,这个参数表示分区的数量
        // 第二个参数可以不传递的,那么makeRDD方法会使用默认值 : defaultParallelism(默认并行度)
        //     scheduler.conf.getInt("spark.default.parallelism", totalCores)
        //    spark在默认情况下,从配置对象中获取配置参数:spark.default.parallelism
        //    如果获取不到,那么使用totalCores属性,这个属性取值为当前运行环境的最大可用核数
        //val rdd = sc.makeRDD(List(1,2,3,4),2)
        
    • 文件创建,和分区规则,数值的去向

      1. 文件创建

        //   在文件中创建,相对路径就是从项目文件开始
        //   既可以指向一个目录名字则计算一个目录下的多个文件,又可以计算单个文件
        //   也可以用通配符 如1*.txt 条件下的文件
        
        val value: RDD[String] = context.textFile("data")
        //    可以显示路径和数据两个值
        
        				// textFile : 以行为单位来读取数据,读取的数据都是字符串
                // wholeTextFiles : 以文件为单位读取数据
                //    读取的结果表示为元组,第一个元素表示文件路径,第二个元素表示文件内容
                val rdd = sc.wholeTextFiles("datas")
        
      2. 分区规则

        //TODO创建RDD
        // textFile可以将文件作为数据处理的数据源,默认也可以设定分区。
        //     minPartitions : 最小分区数量
        //     math.min(defaultParallelism, 2)
        //val rdd = sc.textFile("datas/1.txt")
        // 如果不想使用默认的分区数量,可以通过第二个参数指定分区数
        // Spark读取文件,底层其实使用的就是Hadoop的读取方式
        // 分区数量的计算方式:  7是字节  字节也包括回车符号  2是分区数
        //    totalSize = 7
        //    求一行字节数
        //    goalSize =  7 / 2 = 3(byte)
        //    7 / 3 = 2...1 (1.1) + 1 = 3(分区)
        
        //
        val rdd = sc.textFile("datas/1.txt", 2)
        
      3. 分区后数值的分配

        //TODO创建RDD
        //TODO数据分区的分配
        // 1. 数据以行为单位进行读取
        //    spark读取文件,采用的是hadoop的方式读取,所以一行一行读取,和字节数没有关系
        // 2. 数据读取时以偏移量为单位,偏移量不会被重复读取  @@表示回车符,一行三个字节!
        //    ps :以第一行为多少字节为偏移量范围单位  如一行三个字节则 分区0 为  [0,3]范围的字节
        /*
           1@@   => 012
           2@@   => 345
           3     => 6
         */
        // 3. 数据分区的偏移量范围的计算  从0开始,范围都是闭合
        // 0 => [0, 3]  => 12
        // 1 => [3, 6]  => 3
        // 2 => [6, 7]  =>
        // 【1,2】,【3】,【】
        val rdd = sc.textFile("datas/1.txt", 2)
        
      4. 简单案例

        //总数为14个字节
        // 14byte / 2 = 7byte
        // 14 / 7 = 2(分区)
        
        /*
        1234567@@  => 012345678
        89@@       => 9101112
        0          => 13
        
        [0, 7]   => 1234567
        [7, 14]  => 890
        
         */
        
        // 如果数据源为多个文件,那么计算分区时以文件为单位进行分区
        val rdd = sc.textFile("datas/word.txt", 2)
        
  • RDD算子

    1. rdd的计算,在一个分区内,map算子顺序是计算一个数据完后再计算下一个数据,分区内数据的执行是有序的,例如map

    2. 不同分区之间的执行是无序的

    3. mapPartitions() 以分区为一个缓存区,即有一个分区执行一次而不是map算子每个数据执行一次,mapPartitions()里面参数是迭代器 

       

    <aside> 💡 但是这个mapPartitions()算子完成后是不会释放内存的,所以在内存小,数据较大的情况下,很容易内存溢出

    </aside>

    1. mapPartitionsWithIndex(),这里有两个参数,第一个为分区下标,第二个为迭代器

      Nil.interactor 这个Nil是空值的意思,有许多扩展

      TraversableOnce[U] 这个为课迭代集合

    2. glom() 将RDD个体组成数组,变为数组后就有许多的方法例如就最大值 max

      //TODO算子 - glom
      val rdd : RDD[Int] = sc.makeRDD(List(1,2,3,4), 2)
      val glomRDD: RDD[Array[Int]] = rdd.glom()
      
      glomRDD.collect().foreach(data=> println(data.mkString(",")))
      
      
    3. groupBy() 参数是一个判断函数,此参数函数需要参数是根据需求填类型,返回值是分组判断,记得常用的是匿名参数

      Ps : 分组和分区之间没有必要关系

      // groupBy会将数据源中的每一个数据进行分组判断,根据返回的分组key进行分组
      // 相同的key值的数据会放置在一个组中
      def groupFunction(num:Int) = {
          num % 2
      }
      val groupRDD: RDD[(Int, Iterable[Int])] = rdd.groupBy(groupFunction)
      
      map算子也能大括号运用
      timeRDD.map{
                  case ( hour, iter ) => {
                      (hour, iter.size)
                  }
              }.collect.foreach(println)
      

      <aside> 💡 groupBy()会将数据打乱,重新组合,这和操作被称为shuffle

      </aside>

    4. filter()算子,过滤内容参数是一个判断函数,如果是真则返回,是假就不返回

      rdd.filter(
          line => {
              val datas = line.split(" ")
              val time = datas(3)
              time.startsWith("17/05/2015")
          }
      ).collect().foreach(println)
      
    5. simple()算子,英文名是样品,算子有三个参数

      • 代码例子

                // sample算子需要传递三个参数
                // 1. 第一个参数表示,抽取数据后是否将数据返回 true(放回),false(丢弃)
                // 2. 第二个参数表示,
                //         如果抽取不放回的场合:数据源中每条数据被抽取的概率!!,基准值的概念  
                //         如果抽取放回的场合:表示数据源中的每条数据被抽取的可能次数
        				//         可能大于这个次数也可能小于这个次数
                // 3. 第三个参数表示,抽取数据时随机算法的种子,ps:就是随即一次后会产生一个种子数,如果使用了当前种子数,那随机数将不变
                //                    如果不传递第三个参数,那么使用的是当前系统时间
        //        println(rdd.sample(
        //            false,
        //            0.4
        //            //1
        //        ).collect().mkString(","))
        
                println(rdd.sample(
                    true,
                    2
                    //1
                ).collect().mkString(","))
        
    6. distinct() 去重算子

      算子去重原理和小案例

      val rdd = sc.makeRDD(List(1,2,3,4,1,2,3,4))
      
      // map(x => (x, null)).reduceByKey((x, _) => x, numPartitions).map(_._1)
      
      // (1, null),(2, null),(3, null),(4, null),(1, null),(2, null),(3, null),(4, null)
      // (1, null)(1, null)(1, null)
      // (null, null) => null
      // (1, null) => 1
      val rdd1: RDD[Int] = rdd.distinct()
      
      rdd1.collect().foreach(println)
      
      
    7. coalesce :缩减分区 , repartition()增加分区

      //coalesce
      val rdd = sc.makeRDD(List(1,2,3,4,5,6), 3)
      
              // coalesce方法默认情况下不会将分区的数据打乱重新组合
              // 这种情况下的缩减分区可能会导致数据不均衡,出现数据倾斜
              // 如果想要让数据均衡,可以进行shuffle处理,参数第二个为是否要shuffle
              //val newRDD: RDD[Int] = rdd.coalesce(2)
              val newRDD: RDD[Int] = rdd.coalesce(2,true)
      
      //repartition
      val rdd = sc.makeRDD(List(1,2,3,4,5,6), 2)
              // coalesce算子可以扩大分区的,但是如果不进行shuffle操作,是没有意义,不起作用。
              // 所以如果想要实现扩大分区的效果,需要使用shuffle操作
              // spark提供了一个简化的操作
              // 缩减分区:coalesce,如果想要数据均衡,可以采用shuffle
              // 扩大分区:repartition, 底层代码调用的就是coalesce,而且肯定采用shuffle
              //val newRDD: RDD[Int] = rdd.coalesce(3, true)
              val newRDD: RDD[Int] = rdd.repartition(3)
      
    8. sortBy() 排序,第一个参数为函数,第二个参数可以改变排序的方式,默认为升序

      //TODO算子 - sortBy
      val rdd = sc.makeRDD(List(("1", 1), ("11", 2), ("2", 3)), 2)
      
      // sortBy方法可以根据指定的规则对数据源中的数据进行排序,默认为升序,第二个参数可以改变排序的方式
      // sortBy默认情况下,不会改变分区。但是中间存在shuffle操作
      val newRDD = rdd.sortBy(t=>t._1.toInt, false)
      
      newRDD.collect().foreach(println)
      
      
    9. TODO 算子 - 双Value类型,interection()交集,union()并集,subtract()差集,zip()拉链

      • 双Value类型

        // 交集,并集和差集要求两个数据源数据类型保持一致
        // 拉链操作两个数据源的类型可以不一致
        
        val rdd1 = sc.makeRDD(List(1,2,3,4))
        val rdd2 = sc.makeRDD(List(3,4,5,6))
        val rdd7 = sc.makeRDD(List("3","4","5","6"))
        
        // 交集 : 【3,4】
        val rdd3: RDD[Int] = rdd1.intersection(rdd2)
        //val rdd8 = rdd1.intersection(rdd7)
        println(rdd3.collect().mkString(","))
        
        // 并集 : 【1,2,3,4,3,4,5,6】
        val rdd4: RDD[Int] = rdd1.union(rdd2)
        println(rdd4.collect().mkString(","))
        
        // 差集 : 【1,2】
        val rdd5: RDD[Int] = rdd1.subtract(rdd2)
        println(rdd5.collect().mkString(","))
        
        // 拉链 : 【1-3,2-4,3-5,4-6】
        val rdd6: RDD[(Int, Int)] = rdd1.zip(rdd2)
        val rdd8 = rdd1.zip(rdd7)
        println(rdd6.collect().mkString(","))
        

        <aside> 💡 拉链zip注意事项

        // Can't zip RDDs with unequal numbers of partitions: List(2, 4)
        // 两个数据源要求分区数量要保持一致
        // Can only zip RDDs with same number of elements in each partition
        // 两个数据源要求分区中数据数量保持一致
        val rdd1 = sc.makeRDD(List(1,2,3,4,5,6),2)
        val rdd2 = sc.makeRDD(List(3,4,5,6),2)
        
        val rdd6: RDD[(Int, Int)] = rdd1.zip(rdd2)
        println(rdd6.collect().mkString(","))
        

        </aside>

    10. partitionBy根据指定的分区规则对数据进行重分区

      //TODO算子 - (Key - Value类型)
      val rdd = sc.makeRDD(List(1,2,3,4),2)
      
      val mapRDD:RDD[(Int, Int)] = rdd.map((_,1))
      // RDD => PairRDDFunctions
      // 隐式转换(二次编译)
      
      // partitionBy根据指定的分区规则对数据进行重分区,首先判断是否是同个方法,然后判断分区数量
      val newRDD = mapRDD.partitionBy(new HashPartitioner(2))
      newRDD.partitionBy(new HashPartitioner(2))
      
    11. reduceByKey : 相同的key的数据进行value数据的聚合操作

      // scala语言中一般的聚合操作都是两两聚合,spark基于scala开发的,所以它的聚合也是两两聚合
      // 【1,2,3】
      // 【3,3】
      // 【6】
      // reduceByKey中如果key的数据只有一个,是不会参与运算的。
      val rdd = sc.makeRDD(List(
                  ("a", 1), ("a", 2), ("a", 3), ("b", 4)
              ))
      val reduceRDD: RDD[(String, Int)] = rdd.reduceByKey( (x:Int, y:Int) => {
          println(s"x =${x}, y =${y}")
          x + y
      } )
      
      
    12. groupByKey : 将数据源中的数据,相同key的数据分在一个组中,形成一个对偶元组

      val rdd = sc.makeRDD(List(
          ("a", 1), ("a", 2), ("a", 3), ("b", 4)
      ))
      
      // groupByKey : 将数据源中的数据,相同key的数据分在一个组中,形成一个对偶元组
      //              元组中的第一个元素就是key,
      //              元组中的第二个元素就是相同key的value的集合
      val groupRDD: RDD[(String, Iterable[Int])] = rdd.groupByKey()
      groupRDD.collect().foreach(println)
      
      // groupBy : 将数据源中的数据,相同key的数据分在一个组中,形成一个key,迭代器(key+value)形式
      //              元组中的第一个元素就是key,
      //              元组中的第二个元素就是相同key的迭代器(key+value)形式
      //参数一是根据元素什么值来作为key
      val groupRDD1: RDD[(String, Iterable[(String, Int)])] = rdd.groupBy(_._1)
      

RDD总结

  • RDD特点
    1. rdd是最小的执行计算单元和最小数据处理模型,有读取和分区(分开计算)操作

    2. rdd计算逻辑是不可变的

    3. 可分区,并行计算

       

    4. RDD核心属性

      1. 属性有以下

       

      • 分区列表

      RDD 数据结构中存在分区列表,用于执行任务时并行计算,是实现分布式计算的重要属性。

       

      • 分区计算函数

      Spark 在计算时,是使用分区函数对每一个分区进行计算

       

      ps:就是将计算逻辑到每个分区中计算,而不是计算数据到每个分区中计算

       

      • RDD 之间的依赖关系

      RDD 是计算模型的封装,当需求中需要将多个计算模型进行组合时,就需要将多个 RDD 建立依赖关系

      ps:每个分区都有就算,计算逻辑一样

       

      • 分区器(可选)

      当数据为 KV 类型数据时,可以通过设定分区器自定义数据的分区

       

      • 首选位置(可选)计算数据时,可以根据计算节点的状态选择不同的节点位置进行计算

      ps: 也就是装饰者模式,当要再加一个逻辑计算的时候,是嵌套方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值