Spark_05

回顾

cache方法,没有生成新的RDD,也没有触发任务执行,只会标记该RDD分区对应的数据(第一次触发Action时)放入到内存
checkpoint方法,没有生成新的RDD,也是没有触发Action,也是标记以后触发Action时会将数据保存到HDFS中
根据IP地址计算归属地
IP转换成十进制
二分法查找
广播变量(广播出去的内容一旦广播出去,就不能改变了),如果需要实时改变的规则,可以将规则放到Redis
foreach,foreachPartition是Action,会触发任务提交。(Action的特点是会触发任务,有数据计算的结果产生)
collect,take,count(收集到Driver端的Action)

自定义排序

方法一

将数据封装到对象中,通过对象比较大小来进行排序。

package day5

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object CustomSort1 {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("CustomSort1").setMaster("local[3]")
    val sc = new SparkContext(conf)

    //排序规则:首先按照颜值的降序,如果颜值相等,再按照年龄的升序
    val users = Array("laoduan 30 99", "laozhao 29 9999", "laozhang 28 999", "laoyang 29 99")
    //将Driver端的数据并行化变成RDD
    val lines: RDD[String] = sc.parallelize(users)
    //切分整理数据
    val userRDD: RDD[User] = lines.map(line => {
      val fields = line.split(" ")
      val name = fields(0)
      val age = fields(1).toInt
      val fv = fields(2).toInt
      //(name, age, fv)
      new User(name, age, fv)
    })
    //只是按照颜值进行排序。不满足要求
    //tpRDD.sortBy(tp => tp._3, false)
    //将RDD里面装的User类型的数据进行排序
    val sorted = userRDD.sortBy(u => u)

    val r = sorted.collect()

    println(r.toBuffer)

    sc.stop()
  }
}

class User(val name: String, val age: Int, val fv: Int) extends Ordered[User] with Serializable {
  override def compare(that: User): Int = {
    if(this.fv == that.fv){
      this.age - that.age
    }else{
      -(this.fv -that.fv)
    }
  }
  override def toString: String = s"name: $name, age: $age, fv: $fv"
}
方式二

通过传入排序规则,对元组中的数据进行排序。

package day5
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object CustomSort2 {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("CustomSort2").setMaster("local[3]")
    val sc = new SparkContext(conf)
    //排序规则:首先按照颜值的降序,如果颜值相等,再按照年龄的升序
    val users = Array("laoduan 30 99", "laozhao 29 9999", "laozhang 28 999", "laoyang 29 99")
    //将Driver端的数据并行化变成RDD
    val lines: RDD[String] = sc.parallelize(users)
    //切分整理数据
    val tpRDD: RDD[(String, Int, Int)] = lines.map(line => {
      val fields = line.split(" ")
      val name = fields(0)
      val age = fields(1).toInt
      val fv = fields(2).toInt
      (name, age, fv)
    })
    //排序(传入了一个排序规则,不会改变数据的格式,只会改变顺序)
    val sorted: RDD[(String, Int, Int)] = tpRDD.sortBy(tp => new Boy(tp._2, tp._3))
    val r = sorted.collect()
    println(r.toBuffer)

    sc.stop()
  }
}
class Boy(val age: Int, val fv: Int) extends Ordered[Boy] with Serializable {
  override def compare(that: Boy): Int = {
    if(this.fv == that.fv){
      this.age - that.age
    }else{
      -(this.fv -that.fv)
    }
  }
}
方法三

通过样例类传递比较规则。

package day5
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object CustomSort3 {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("CustomSort3").setMaster("local[3]")
    val sc = new SparkContext(conf)

    //排序规则:首先按照颜值的降序,如果颜值相等,再按照年龄的升序
    val users = Array("laoduan 30 99", "laozhao 29 9999", "laozhang 28 999", "laoyang 29 99")
    //将Driver端的数据并行化变成RDD
    val lines: RDD[String] = sc.parallelize(users)

    //切分整理数据
    val tpRDD: RDD[(String, Int, Int)] = lines.map(line => {
      val fields = line.split(" ")
      val name = fields(0)
      val age = fields(1).toInt
      val fv = fields(2).toInt
      (name, age, fv)
    })
    //排序(传入了一个排序规则,不会改变数据的格式,只会改变顺序)
    val sorted: RDD[(String, Int, Int)] = tpRDD.sortBy(tp => Man(tp._2, tp._3))
    val r = sorted.collect()
    println(r.toBuffer)
    sc.stop()
  }
}
//样例类,默认实现了序列化。
case class Man(age: Int, fv: Int) extends Ordered[Man] {
  override def compare(that: Man): Int = {
    if(this.fv == that.fv){
      this.age - that.age
    }else{
      -(this.fv -that.fv)
    }
  }
}
方法四

通过导入隐式转换进行排序。

package day5

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

object CustomSort4 {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("CustomSort4").setMaster("local[3]")
    val sc = new SparkContext(conf)
    //排序规则:首先按照颜值的降序,如果颜值相等,再按照年龄的升序
    val users = Array("laoduan 30 99", "laozhao 29 9999", "laozhang 28 999", "laoyang 29 99")
    //将Driver端的数据并行化变成RDD
    val lines: RDD[String] = sc.parallelize(users)

    //切分整理数据
    val tpRDD: RDD[(String, Int, Int)] = lines.map(line => {
      val fields = line.split(" ")
      val name = fields(0)
      val age = fields(1).toInt
      val fv = fields(2).toInt
      (name, age, fv)
    })
    //排序(传入了一个排序规则,不会改变数据的格式,只会改变顺序)
    import SortRules.OrderingXianRou    //调入隐式转换
    val sorted: RDD[(String, Int, Int)] = tpRDD.sortBy(tp => XianRou(tp._2, tp._3))
    val r = sorted.collect()
    println(r.toBuffer)
    sc.stop()
  }
}
//样例类
case class XianRou(age: Int, fv: Int)

object SortRules {
  implicit object OrderingXianRou extends Ordering[XianRou]{
    override def compare(x: XianRou, y: XianRou): Int = {
      if(x.fv == y.fv){
        x.age - y.age
      }else{
        y.fv -x.fv
      }
    }
  }
}
方法五

利用元组的比较规则进行比较。

package day5

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

object CustomSort5 {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("CustomSort5").setMaster("local[3]")
    val sc = new SparkContext(conf)

    //排序规则:首先按照颜值的降序,如果颜值相等,再按照年龄的升序
    val users = Array("laoduan 30 99", "laozhao 29 9999", "laozhang 28 999", "laoyang 29 99")
    //将Driver端的数据并行化变成RDD
    val lines: RDD[String] = sc.parallelize(users)
    //切分整理数据
    val tpRDD: RDD[(String, Int, Int)] = lines.map(line => {
      val fields = line.split(" ")
      val name = fields(0)
      val age = fields(1).toInt
      val fv = fields(2).toInt
      (name, age, fv)
    })
    //充分利用元组的比较规则,元组的比较规则:先比第一个,相等再比第二个
    val sorted: RDD[(String, Int, Int)] = tpRDD.sortBy(tp => (-tp._3, tp._2))
    val r = sorted.collect()
    println(r.toBuffer)
    sc.stop()
  }
}
方法六

在上下文搜索隐式转换,通过隐式转换,改变元组的数据格式。

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object CustomSort6 {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("CustomSort6").setMaster("local[3]")
    val sc = new SparkContext(conf)
    //排序规则:首先按照颜值的降序,如果颜值相等,再按照年龄的升序
    val users = Array("laoduan 30 99", "laozhao 29 9999", "laozhang 28 999", "laoyang 29 99")
    //将Driver端的数据并行化变成RDD
    val lines: RDD[String] = sc.parallelize(users)

    //切分整理数据
    val tpRDD: RDD[(String, Int, Int)] = lines.map(line => {
      val fields = line.split(" ")
      val name = fields(0)
      val age = fields(1).toInt
      val fv = fields(2).toInt
      (name, age, fv)
    })

    //Ordering[(Int, Int)]最终比较的规则格式
    //on[(String, Int, Int)]未比较之前的数据格式
    //(t=>(-t._3, t._2))怎样将规则转换成想要比较的规则。
    implicit val rules = Ordering[(Int, Int)].on[(String, Int, Int)](t => (-t._3, t._2))
    val sorted: RDD[(String, Int, Int)] = tpRDD.sortBy(tp => tp)
    val r = sorted.collect()
    println(r.toBuffer)

    sc.stop()
  }
}

JDBC RDD

hadoop处理方式:关系型数据库==》分布式文件系统==》运用yarn对数据进行mapreduce

JDBC RDD优点:可以直接从关系型数据库中读取数据。不用再使用sqoop数据迁移工具。

package day5
import java.sql.DriverManager
import org.apache.spark.rdd.{JdbcRDD, RDD}
import org.apache.spark.{SparkConf, SparkContext}

object JdbcRDDDemo {

  val getConn = () => {
    DriverManager.getConnection("jdbc:mysql://localhost:3306/spark_about?characterEncoding=UTF-8","root","123")
  }

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("JDBC_RDD").setMaster("local[3]")
    val sc = new SparkContext(conf)

    //创建RDD,这个RDD会记录以后从MySql中读取数据
    //new RDD后,里面没有真正的数据,而是告诉这个RDD,以后触发runJob时,以后从什么地方读取数据。
    val jdbcRDD: RDD[(Int, String, Int)] = new JdbcRDD(
      sc,
      getConn,
      "SELECT * FROM logs WHERE id > ? AND id <= ?",
      1,//范围下线
      5,//范围上线
      2,//分区数量
      rs => {
        val id = rs.getInt(1)
        val name = rs.getString(2)
        val age = rs.getInt(3)
        (id, name, age)
      }
    )

    //触发Action
    val r = jdbcRDD.collect()
    println(r.toBuffer)

    sc.stop()
  }
}

注意事项:当使用SQL语句:SELECT * FROM logs WHERE id >= ? AND id < ?,进行查询时,得到的结果为:ArrayBuffer((1,张三,18), (3,王五,22), (4,赵六,19)),下图可以解释这种结果出现的原因。
在这里插入图片描述

Spark的执行过程简介

在这里插入图片描述

Spark任务执行的流程,四个步骤

1.构建DAG(调用RDD上的方法)
2.DAGScheduler将切分Stage,切分的依据是Shuffle,将Stage中生成的Task以TaskSet的形式给TaskScheduler
3.TaskScheduler调度Task(根据资源情况将Task调度到相应的Executor中)
4.Executor接收Task,然后将Task丢入到线程池中执行。

DAG有向无环图流程如下图:(有方向,没有闭环)

1.DAG描述多个RDD的转换过程,任务执行时,可以按照DAG的描述,执行正真的计算(数据被操作的一个过程)。
2.DAG是有边界的:开始(通过SparkContext创建的RDD),结束(触发Action,调用runJob就是一个完整的DAG就形成了,一旦触发Action就形成了一个完整的DAG)。
3.一个Spark Application中是有多少个DAG:一到多个(取决于触发了多少次Action)
4.在一个DAG中可能产生多种不同类型和功能的Task,会有不同的阶段。
5.一个RDD只是描述了数据计算过程中的一个环节,而DAG由一到多个RDD组成,描述了数据计算过程中的所有环节(过程)

在这里插入图片描述

DAGScheduler

将一个DAG切分成一到多个Stage,DAGScheduler切分的依据是Shuffle(宽依赖)。
为什么要切分Stage?
一个复杂的业务逻辑(将多台机器上具有相同属性的数据,聚合到一台机器上:shuffle)要分阶段进行。如果有Shuffle,那么就意味着前面阶段产生的结果后,才能执行下一个阶段,下一个阶段的计算要依赖上一个阶段的数据。在同一个Stage中,会有多个算子可以合并在一起,我们称其为pipeline(流水线:严格按照流程,顺序执行)。

在这里插入图片描述

RDD的依赖关系

RDD和它依赖的父RDD(s)的关系有两种不同的类型,即窄依赖和宽依赖。
在这里插入图片描述
shuffle的定义:
shuffle的含义是洗牌,将数据打散,父RDD的一个分区中的数据,如果给了多个子RDD的分区,就是Shuffle;
shuffle会有网络传输数据,但是可有网络传数据并不意味着就是Shuffle(窄依赖也会有网络传输);

join

分区中的数据必须是(k,v)类型才能join。
在这里插入图片描述
上图执行过程中:产生了3个Stage,6个Task。是宽依赖。
在这里插入图片描述
val rdd1 = sc.parallelize(List((“tom”, 1),(“kitty”, 1),(“tom”, 2),(“kitty”,2)), 2)
val rdd2 = sc.parallelize(List((“tom”, 3),(“kitty”, 3),(“tom”, 4),(“kitty”,4)), 2)
val rdd3 = rdd1.groupByKey()
val rdd4 = rdd2.groupByKey()
val rdd5 = rdd3.join(rdd4)
rdd5.saveAsTextFile(“hdfs://node1:9000/test_join2”)
结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上述产生:3个Stage,6个Task;

线程池

package cn.edu360.spark;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
    /*
    线程池出现的原因:
    在频繁创建线程的情况下,线程的创建和销毁占用比较多的CPU资源,造成了一种浪费。
    线程池的创建可以减少线程的创建与销毁,而且不影响线程的调用。
     */
    public static void main(String[] args){
        //创建一个单线程的线程池
        ExecutorService pool = Executors.newSingleThreadExecutor();
        for(int i = 0; i<=10;i++){
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    //打印当前线程的名称
                    System.out.println(Thread.currentThread().getName());
                    try{
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "is over");
                }
            });
        }
        System.out.println("all Task is submited");
        pool.shutdownNow();//停止当前线程池的使用
    }
}
public class ThreadPoolDemo {

    /*
    线程池出现的原因:
    在频繁创建线程的情况下,线程的创建和销毁占用比较多的CPU资源,造成了一种浪费。
    线程池的创建可以减少线程的创建与销毁,而且不影响线程的调用。
     */
    public static void main(String[] args){
        //固定大小的线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        for(int i = 0; i<=10;i++){
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    //打印当前线程的名称
                    System.out.println(Thread.currentThread().getName());
                    try{
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "is over");
                }
            });
        }
        System.out.println("all Task is submited");
    }
}

结果:
pool-1-thread-1
all Task is submited
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
pool-1-thread-5
pool-1-thread-1is over
pool-1-thread-2is over
pool-1-thread-4is over
pool-1-thread-4
pool-1-thread-3is over
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-5is over
pool-1-thread-5
pool-1-thread-1is over
pool-1-thread-2is over
pool-1-thread-3is over
pool-1-thread-4is over
pool-1-thread-1
pool-1-thread-5is over
pool-1-thread-1is over
分析:线程1-5会出现2次,因为线程池只有5个线程。

public class ThreadPoolDemo {

    /*
    线程池出现的原因:
    在频繁创建线程的情况下,线程的创建和销毁占用比较多的CPU资源,造成了一种浪费。
    线程池的创建可以减少线程的创建与销毁,而且不影响线程的调用。
     */
    public static void main(String[] args){

        //创建一个单线程的线程池
        //ExecutorService pool = Executors.newSingleThreadExecutor();

        //固定大小的线程池
        //ExecutorService pool = Executors.newFixedThreadPool(5);

        //可缓冲的线程池(可以有任意多个线程)
        ExecutorService pool = Executors.newCachedThreadPool();

        for(int i = 0; i<=10;i++){
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    //打印当前线程的名称
                    System.out.println(Thread.currentThread().getName());
                    try{
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "is over");
                }
            });
        }
        System.out.println("all Task is submited");
    }
}

结果:在一瞬间可以给多个线程
pool-1-thread-1
all Task is submited
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
pool-1-thread-5
pool-1-thread-6
pool-1-thread-7
pool-1-thread-8
pool-1-thread-9
pool-1-thread-10
pool-1-thread-11
pool-1-thread-5is over
pool-1-thread-9is over
pool-1-thread-10is over
pool-1-thread-11is over
pool-1-thread-4is over
pool-1-thread-8is over
pool-1-thread-7is over
pool-1-thread-6is over
pool-1-thread-2is over
pool-1-thread-3is over
pool-1-thread-1is over

序列化

应用场景:网络传输,对象保存到磁盘。

package cn.edu360
import java.io.{FileInputStream, FileOutputStream, ObjectInputStream, ObjectOutputStream}
class MapTask extends Serializable{
  //以后重哪里了读取数据
  //以后该如何执行,根据RDD的转换关系(调用那个方法,传入了什么函数)
  def m1(path: String): String = {
    path.toString
  }
  def m2(line: String): Array[String] = {
    line.split(" ")
  }
}

object SerTask {
  def main(args: Array[String]): Unit = {
    //new一个实例,然后打印她的hashcode值
    //在Driver端创建这个实例
    //序列化后发生出去,发送到Executor,Executor接收后,反序列化,用一个实现了Runnable接口一个类包装一下,然后丢到线程池中
    val t = new MapTask
    println(t)
    val oos = new ObjectOutputStream(new FileOutputStream("./t"))
    oos.writeObject(t)
    oos.flush()
    oos.close()
    val ois1 = new ObjectInputStream(new FileInputStream("./t"))
    val o1 = ois1.readObject()
    println(o1)
    ois1.close()
    val ois2 = new ObjectInputStream(new FileInputStream("./t"))
    val o2 = ois2.readObject()
    println(o2)
    ois2.close()
  }
}

Task流程
在Driver端创建这个实例Task,序列化后发送出去,发生个Executor,Executor接收后,反序列化,用一个实现了Runnable接口一个类包装一下,然后丢到线程池中。

Spark运行相关问题

1.SparkContext那一端生成的?
Driver端
2.DAG是在那一端生成的?
Driver端
3.RDD是在那一端生成的?
Driver端
4.广播变量在那一端调用的方法进行广播的?
Driver端
5.要广播的数据应该在那一端创建好再广播呢?
Driver端
6.调用RDD端算子()是在那一端调用的?
Driver端
7.RDD在调用Transformation和Action时需要传入一个函数,函数是在那一端声明和传入的?
Driver端
8.RDD在调用Transformationhe Action时需要传入函数,请问传入的函数是在那一端执行了函数的业务逻辑?
Executor中的Task中调用的
9.自定义的分区器这个类是在那一端实例化的?
Driver端
10.分区器中的getParition方法在那一端调用的呢?
Executor中的Task中调用的
11.Task是在那一端生成的呢?
Driver端
12.DAG是在那一端构建好的并且被切分成一到多个Stage的
Driver端
13.DAG是哪一个类完成的切分Stage的功能?
DAGScheduler
14.DAGScheduler将切分好的Stage以什么样的形式给TaskScheduler
TaskSet

Spark的设计就是基于这个抽象的数据集(RDD),你操作RDD这个抽象的数据集,就像操作一个本地集合一样,Spark包底层的细节都隐藏起来的(任务调度、Task执行,任务失败重试等待),开发者使用起来更加方便简洁。操作RDD,其实是对每个分区进行操作,分区会生成Task,Task会调度Executor上执行相关的计算逻辑,进而对数据进操作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值