Spark快速全面复习(尚硅谷)

简单的先运行一下

object Hello {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local").setAppName("wc")
    val sc=new SparkContext(conf)
    sc.stop()
  }
}

运气不好直接报错
在这里插入图片描述
这个应该是scala版本不对
当前idea引入的scala运行环境版本与idea默认的scala版本不一样
查看本项目的Project Structure,点击Global Libraries选项查看,显示版本一致。就是导入pom.xml的spark那个依赖版本和你导入scala那个版本不对映

数据存放位置

sc.textfile 时,他默认读取的数据的位置是

在这里插入图片描述
放在untiled3下面不能放到spark下,放在spark下需要配置

object Hello {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local").setAppName("wc")
    val sc=new SparkContext(conf)
    val lines : RDD[String] = sc.textFile("datas")
    val word = lines.flatMap(_.split(" "))
    val wordGroup =word.groupBy(word=>word)
    wordGroup.collect().foreach(println)
    val wordCount = wordGroup.map {
      case (word, list) => {
        (word, list.size)
      }
    }

    sc.stop()
  }
}

wordCount的计算,从这可以看出,真的比hadoop方便编程,哪还用map,recuce ,driver?

log4j配置

log4j.rootLogger=ERROR,CONSOLE
log4j.addivity.org.apache=true

# console
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.Encoding=UTF-8
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[demo] %-5p %d{yyyy-MM-dd HH\:mm\:ss} - %C.%M(%L)[%t] - %m%n

# all
log4j.logger.com.demo=INFO, DEMO
log4j.appender.DEMO=org.apache.log4j.RollingFileAppender
log4j.appender.DEMO.File=${catalina.base}/logs/demo.log
log4j.appender.DEMO.MaxFileSize=50MB
log4j.appender.DEMO.MaxBackupIndex=3
log4j.appender.DEMO.Encoding=UTF-8
log4j.appender.DEMO.layout=org.apache.log4j.PatternLayout
log4j.appender.DEMO.layout.ConversionPattern=[demo] %-5p %d{yyyy-MM-dd HH\:mm\:ss} - %C.%M(%L)[%t] - %m%n
   <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.30</version>
    </dependency>

这个网址可以看运行spark的程序执行情况在这里插入图片描述

把程序jar包在本地运行

bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master local[2] \
./examples/jars/spark-examples_2.12-3.1.1.jar \
10

这里说明一下啊, --class 表示那个要运行的类

bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode cluster \
./examples/jars/spark-examples_2.12-3.1.1.jar \
10

在这里插入图片描述

Standlone模式和Yarn模式和本地模式区别

本地就是一台计算机,不分布式,standlone 就是Spark自身提供计算资源,无需其他框架提供资源,这种方式降低了和其它第三方资源框架的耦合性,独立性非常强,但是spark主要是计算框架,而不是资源调度框架,所以一般都是用yarn模式。
Standlone 是自己调度 Master 管两个worker,而Yarn则是RM管NM

配置YARN环境

修改Hadoop中的YARN-site.xml

#把物理内存过大会自动关闭给关了
<property>
        <name>yarn.nodemanager.pmem-check-enabled</name>
        <value>false<value>
</property> 
#把虚拟内存过大会自动关闭给关了
<property>
        <name>yarn.nodemanager.vmem-check-enabled</name>
        <value>false<value>
</property>

修改spark conf中的spark-env.sh.templete 为 spark-env.sh并增加以下代码

export JAVA_HOME=/opt/SoftWare/Java/jdk1.8.0_212
YARN_CONF_DIR=/opt/SoftWare/Hadoop/hadoop-2.7.7/etc/hadoop

配置历史服务器

修改spark-defalults.conf.template为spark-defaults,conf
修改 spark-defalut.conf路径

mapred-site.xml

	<!--Spark Yarn-->
    <property>
        <name>mapreduce.jobhistory.address</name>
        <value>node001:10020</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.webapp.address</name>
        <value>node001:19888</value>
    </property>

yarn-site.xml

	<!--Spark Yarn-->
    <!-- 是否开启聚合日志 -->
    <property>
        <name>yarn.log-aggregation-enable</name>
        <value>true</value>
    </property>
    <!-- 配置日志服务器的地址,work节点使用 -->
    <property>
        <name>yarn.log.server.url</name>
        <value>http://node001:19888/jobhistory/logs/</value>
    </property>
    <!-- 配置日志过期时间,单位秒 -->
    <property>
        <name>yarn.log-aggregation.retain-seconds</name>
        <value>86400</value>
    </property>

1)修改配置文件spark-default.conf,添加信息

spark.yarn.historyServer.address=hadoop100:18080
spark.history.ui.port=18080

这里把历史日志地址选在了hadoop100这台主机上,端口是18080

  1. 在spark-env.sh中添加
export JAVA_HOME=/opt/SoftWare/Java/jdk1.8.0_212
YARN_CONF_DIR=/opt/SoftWare/Hadoop/hadoop-2.7.7/etc/hadoop
HADOOP_CONF_DIR=/opt/SoftWare/Hadoop/hadoop-2.7.7/etc/hadoop
export SPARK_HISTORY_OPTS=" 
-Dspark.history.ui.port=18080
-Dspark.history.fs.logDirectory=hdfs://node001:9000/directory
-Dspark.history.retainedApplications=30"

(3)重启Spark的历史服务

[kevin@hadoop100 spark]$ sbin/stop-history-server.sh
[kevin@hadoop100 spark]$ sbin/start-history-server.sh

Yarn 的工作流程

1)客户端提交应用程序,SparkSubmit

(2)让RM启动Spark的ApplicationMaster程序,用于Spark与Yarn之间资源交互

(3)AM向RM申请资源,用于启动Executor

(4)RM获取集群的资源信息(NM)

(5)RM将资源信息发送给AM,由AM中的Driver判断任务调度的地址

(6)Driver划分任务,分配任务task发送给Executor执行

(7)Executor执行任务,执行完毕后,通知Driver

(8)Driver和AM交互通知RM回收资源

(9)Executor、Container、Driver、ApplicationMaster就都释放资源消失

(10)最终留下Yarn的RM和NM,在client端打印结果。

端口号

在这里插入图片描述

RDD

在这里插入图片描述
rdd是最小逻辑运算单元,driver里包含逻辑和数据
RDD是spark中最基本的数据处理模型,代码中是一个抽象类,他代表一个弹性的(存储的弹性,磁盘和和内存直接可以自动切换,容错的弹性,数据丢失,知道从哪个文件读取,计算的弹性,计算出错重试机制,分片的弹性,可根据需要重新分片),不可变,可分区,里边元素可并行计算的集合。RDD封装了计算逻辑,并不能保存数据

RDD 流程

Yarn启动,假如1个RM,2个NM,RM(driver)NM(excuator) rdd经过组合,在driver中形成task ,多个task 组成taskpoll,然后传输给最优(存储数据)的excuator,

rdd内存中创建

object rddMemory {
  def main(args: Array[String]): Unit = {
    val sparkConf=new SparkConf().setMaster("local[*]").setAppName("")
    val sc=new SparkContext(sparkConf)
    val  seq= Seq(1,2)
    //parallelize 并行的,比如说2核,既1对1关系,如果1核则并发关系
    val rdd=  sc.parallelize(seq)
    //makeRDD底层调用了parallelize,可能是为了方便程序员记忆吧
    sc.makeRDD(seq)
    rdd.collect().foreach(println)
    sc.stop()
  }
}

rdd文件中创建
sc.textfile(hdfs://node001:9000/文件夹) //以行为单位,不管哪个文件
sc.wholeTextFile(路径) 以文件为单位,识别在哪个文件下

RDD的分区

RDD不保存数据,但是他们之间的关系会保存。

sc.makeRdd(list(1,2),5) //5个分区,如果不写5的话,默认为val sparkConf=new SparkConf().setMaster("local[*]").setAppName("")中local[*]中的最大核数。
sc.textfiel(文件,分区数)
/*
 分区数默认为2 ,如果比2小,则采用更小的,如果比2大就进行下面的运算。实际上,底层用的是hadoop,的Textinputformat 读取数据,按照任务进行分区
4个文件长度分别为100 100 100 1400字节,默认最小分区为2
首先计算全部文件总长度totalSize=100+100+100+1400=1700
goalSize=totalSize/最小分区数即2 =850
blockSize=128M换算成字节为134217728
minSize=1
goalSize与blockSize取最小 值为850
850 与minSize取最大 值为850
即splitSize为850
然后 每个文件长度除以850 判断是否大于1.1
文件1,2,3都是100所以各生成1个分区,
文件4位1400,除以850>1.1 切分一个分区,剩余
(1400-850)/850 >1.1不再成立 又生成一个分区.
所以举例中的四个文件 共生成5个分区
*/

转换算子与行动算子

map() 怎么说呢,这个算子如果一个分区的话,他会一个一个的算,如果两个分区的话,它在分区内有序,但是两个分区之间无序。
**mapPatition()**他其实就是相当于一个缓冲区,一个分区算一次,以分区为单位,但是会将整个分区的数据加载到内存进行引用,就是处理完的数据也不会释放掉,因为存在对象的引用,所以说,在面对分区数据大,运行内存小,不可以的哦!例子:比如说取分区中最大的数。里边传的是迭代器,传出的也是迭代器。
mapPartitonsWithIndex

val rdd=RDD.mapPartionWithIndex(
(index,iter)=>(
if(index==1){
	iter
}
else{
Nil.iterator
}
)

FlatMap 返回值为一个可迭代的集合,扁平化操作。

 val conf=new SparkConf().setAppName("").setMaster("local[*]")
     val sc=new SparkContext(conf)
     val rdd=sc.makeRDD(List(List(1,2),4,List(3,5)))
    val res = rdd.flatMap(data =>
      data match {
        case list: List[Int] => list
        case int => List(int)
      }
    )
    res.collect.foreach(println)

glom把一个分区的数据以数组Array的方式封装起来。将同一个分区的数据直接转换为相同类型的内存数组进行处理,分区不变

val conf=new SparkConf().setAppName("").setMaster("local[*]")
     val sc=new SparkContext(conf)
     val rdd=sc.makeRDD(List(List(1,2),4,List(3,5)))

    rdd.glom().foreach(data=>println(data.mkString(",")))

groupBy(fun) fun是分组的格式如(%2),返回值类型是(分组号,ComPactBuffer(数据)) 再比如首字母放到一块 groupBy(.charAt(0))
filter
sample 三个参数(是否放回,每条数据被抽取的概率,抽取随机数种子)
distinct 去重
coalesce rdd.colesce(3,true)重新分区,平均分配。第一个参数是分区数,第二个分区数是是否shuffle
,一般用于缩小分区
repartition 扩大分区,底层是coalesce
sortby 排序,中间存在shuffle操作,就是分区中的数据变化了 sortBy(_._2)(Ordering.Int.reverse).take(3)倒序取前三个
交并差 rdd1.intersection(rdd2)
rdd1.union(rdd2)
rdd1.substact(rdd2)
rdd1.zip(rdd2)拉链操作 注意分区数量一直才能拉链,并且分区内元素数量也得相同
partitionBy() 把数据修改分区,该数据必须是key-value 并且rdd.partitionBy(new HashPartitioner(2))
groupbykey和reduceBykey 后者在shuffle之前先预聚合了一下,可以减少落盘时的数据量,所以效率高,但是不需要聚合的话,一般用groupBykey
aggregateBykey

     val rdd=sc.makeRDD(List(("a",1),("a",2),("a",3),("a",4)),2)
    //第一个值为初始值0
    //求两个区间最大值相加
    //第二个值为每个分区内的计算,(x,y)x表示初始值,y表示Value
    //第三个值分区间的计算 (x,y) 表示初始值形式的分区数据
    rdd.aggregateByKey(0)((x,y)=>math.max(x,y),(x,y)=>(x+y)).collect.foreach(print)

如果分区内和分区间计算规则相同,rdd.foldByKey(0)(+)
combineByKey 和aggregateByKey的区别就是第一个参数表示将Value的值转换为想要的数据。
join 相同的key笛卡尔乘积,依此匹配
leftOuterJoin
cogroup 可以理解为connect group

算子以外的代码都是在driver运行,算子里边的代码都是在Executor端执行,因此类一定要注意序列化啊

Kyro 序列化

血缘关系

RDD不保存数据,但是保存血缘 rdd.todebugString 看其血缘
**

窄依赖宽依赖

RDD只有一个孩子,叫窄依赖 onebyone

RDD任务划分

Application 初始化一个SparkContext就生成一个Application
Job 一个行动算子就会生成一个job
stage 一个宽依赖既一个Shuffle就能生成一个阶段
Task,一个stage中,最后一个rdd分区个数就是任务数

cache persist checkpoint持久化操作

区别,cache保存到内存中,persist(StorageLevel.级别)cache底层是默认的persist,其实就是persist的Memory-only模式,而checkpoint存到磁盘中

package com.zhenghaozhe

import com.esotericsoftware.kryo.serializers.DefaultSerializers.KryoSerializableSerializer
import com.esotericsoftware.kryo.serializers.JavaSerializer
import org.apache.commons.configuration.Configuration
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object Hello {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("ChickPoint").setMaster("local[*]")
    val sc = new SparkContext(conf)
    //设置检查点目录,可以是HDFS等文件系统
    sc.setCheckpointDir("E:/checkpoint")
    val rdd1 = sc.makeRDD(Array("Spark"))
    val rdd2_source = rdd1.map(_+":"+System.currentTimeMillis())
    val rdd2_cache = rdd1.map(_+":"+System.currentTimeMillis())
    val rdd2_check = rdd1.map(_+":"+System.currentTimeMillis())
    println("-----------没有设置 Checkpoint-----------")
    rdd2_source.foreach(println)
    rdd2_source.foreach(println)
    rdd2_source.foreach(println)
    println("-----------设置 Cache 后-----------")
    rdd2_cache.cache()
    rdd2_cache.foreach(println)
    rdd2_cache.foreach(println)
    rdd2_cache.foreach(println)
    println("-----------设置 Checkpoint 后-----------")
    rdd2_check.checkpoint()
    rdd2_check.foreach(println)
    rdd2_check.foreach(println)
    rdd2_check.foreach(println)
    rdd2_check.foreach(println)
    rdd2_check.foreach(println)
    sc.stop()

  }
}

**

自定义分区

继承Partitoner
通过RDD.partionBy(new MyPartion)来指定分区

累加器

累加器是把excutor聚合给driver端,本来只能从driver传给excutor

package com.zhenghaozhe.rdd

import java.util.concurrent.atomic.LongAccumulator

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

object rddAcc {
  def main(args: Array[String]): Unit = {
    val conf=new SparkConf().setMaster("local[*]").setAppName("")
    val sc=new SparkContext(conf)
    var rdd=sc.makeRDD(List(1,2,3,4))
    var rddAcc=sc.longAccumulator("累加器")
    //注意一个问题,累加器它是由sc掉用的,累加器必须要有而且只能有一个行动算子,如果两个的话,结果就会多加、
    //没有行动算子的话就会少加
      rdd.foreach(
      sum=>{
        rddAcc.add(sum)
        sum
      }
    )

    println(rddAcc.value)

  }

}

广播变量

val conf = new SparkConf()
conf.setMaster("local").setAppName("brocast")
val sc = new SparkContext(conf)
val list = List("hello hadoop")
val broadCast = sc.broadcast(list)
val lineRDD = sc.textFile("./words.txt")
lineRDD.filter { x => broadCast.value.contains(x) }.foreach { println}
sc.stop()

driver中的广播变量发送给每个excutor,作为公共部分,每一个task都可以读取到,这样节省了内存空间,多个task共同读取一个。

大数据三层架构

dao(持久层链接数据库) service(服务层) control(控制层)
MVC(model view Control) 最早是servlet(HTML CSS)后来jsp,后来发现太混乱 MVC

SparkSQL

Hive是把MR简化,Sparksql是把RDD简化。
DataFrame就是带schema信息,而RDD则是冰冷的数据,dataSet则是针对于对象
rdd df ds 三者的转换

package com.zhenghaozhe.sparksql

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

object sql {
  def main(args: Array[String]): Unit = {
    val sc=new SparkConf().setMaster("local[*]").setAppName("")
    val ss=SparkSession.builder().config(sc).getOrCreate()
    //DataFrame
    val df = ss.read.json("datas/1.txt")
    df.show()
    ///
    df.createTempView("user")
    ss.sql("select age from user").show
    ///
    //在使用DataFrame时,如果涉及到转换操作,需要引入转换规则
    import ss.implicits._
    df.select("age").show
    df.select($"age"+1).show
    df.select('age+1).show
    //DataSet
    val sea=Seq(1,2,30)
    sea.toDS().show
    //RDD<=>DataFrame
    val rdd1=ss.sparkContext.makeRDD(List((1,"asd"),(2,"cff")))
    val df1=rdd1.toDF("age","name")
    //DataFrame<=>DataSet
   val DS =df1.as[User].show
    //RDD<=>DataSet
    rdd1.map{
      case(age,name)=>{
        User(age , name )
      }
    }.toDS()
    //TODO关闭环境
    ss.close()

  }
  case class User(age:Int,name:String)

}

udAf spark3.0之前自定义函数用UserDefinedAggregateFunction,之后推荐使用Aggregator

    val sc = new SparkConf().setAppName("").setMaster("local[*]")
    val ss = SparkSession.builder().config(sc).getOrCreate()
    val DF = ss.read.json("E:\\work\\untitled3\\datas\\1.txt")
    DF.createTempView("user")
    ss.udf.register("prefix",(name:String)=>{
      "name:"+name
    }
    )
    ss.sql("select prefix(name) from user").show

读取和保存
ss.read.load(路径) ss.write.save(路径)默认保存和读取都是parquet类型

SparkStrming

windows 下nc不会安装啊,老是说nc.exe有病毒,没法复制,最后只好用虚拟机上的了。

 val sc=new SparkConf().setMaster("local[*]").setAppName("")
    val ssc=new StreamingContext(sc,Seconds(3))
   val line= ssc.socketTextStream("192.168.18.101",9999)
    line.flatMap(_.split(" ")).print
    ssc.start()
    ssc.awaitTermination()

自定义数据采集器

def main(args: Array[String]): Unit = {
  val sc=new SparkConf().setMaster("local[*]").setAppName("")
    val ssc=new StreamingContext(sc,Seconds(3))
    val value = ssc.receiverStream(new MyReceiver())
      value.print()
    ssc.start()
    ssc.awaitTermination()
  }
  //自定义数据采集器
  class MyReceiver extends Receiver(StorageLevel.MEMORY_ONLY){
    private var flag=true
    override def onStart(): Unit ={
      new Thread(new Runnable {
        override def run(): Unit = {
          while(flag){
            val message="采集数据为"+new Random().nextInt(10).toByte
                store(message)
            Thread.sleep(500)
          }
        }
      }).start()
    }

    override def onStop(): Unit = {
      flag=false
    }
  }

有状态转换和无状态转换
在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值