Spark_Core

本文详细介绍了Spark的主要特点和运行模式,包括其在内存计算的优势、RDD的特性和操作,以及各种转换和行动算子如groupByKey、reduceByKey、join等的使用。此外,还探讨了Spark的缓存策略和持久化级别,强调了如何优化数据处理流程。
摘要由CSDN通过智能技术生成

网络<磁盘<内存
1.spark将数据读到内存中然后进行查询,底层是mr代码简洁,运行速度快
2.四种运行模式: Local多用于测试 Standalone(saprk独立集群) Mesos YARN最具前景(公司主流形式)

3.scala是单机的,spark可以是分布式的

引入依赖     scala三件套外加下面一个
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.11</artifactId>
            <version>2.4.5</version>
        </dependency>

local:存储文件失败:本地部署
在本地Hadoop的bin下加上winutils插件,配置环境变量

1.RDD小知识点

RDD:弹性的分布式数据集
弹性:可以很多可以很少
分布式:数据分布在多台服务器中
数据集:从RDD中流过,边读边处理边输出
spqrk采用的也是mr读取数据的方法
block对应RDD中的分区,分区数越多并行度越高,在资源充足的情况下效率越高
RDD分区数在读取文件时默认等于切片的数量,生成分区数是大于最小分区数的3的倍数,小于3则为3,保证每一个分区数据量是差不多的      (sc.textFile("path",数量))
在mr上进行封装,代码简洁,可以读取一个目录下的文件
shuffle阶段,数据会落地到磁盘生成磁盘小文件
窄依赖的算子不能改变分区数,默认等于前一个rdd的分区数,宽依赖默认也是等于前面的,在shuffle阶段可以重新设置分区数

2.RDD五大特性

RDD五大特性:
1.LinesRDD由一组分区组成,读取文件默认一个block块对应一个分区,后面分区数和前面相同
2.函数(代码)作用在每个分区上的,有多少分区就有多少task(一一对应)     
3.后面的RDD依赖前面的RDD,有shuffle的(宽依赖)宽依赖前是一个stage(map或者reduce端)
4.分区内的算子只能作用在kv格式的RDD上    groupbykey()         
5.spark为task的计算提供了最佳的计算位置

3.算子(方法) 转换transformation 操作action

1.配置spark的基本四步操作

//获取SparkContext对象   有两种方法
val conf=new SparkConf()
    conf.setMaster("local")
    conf.setAppName("map")
    val sc=new SparkContext(conf)

2.Transformation

map
filter
flatMap
sample    抽样

1.group和groupByKey
group和groupByKey的区别:
1.groupBy可以在任何类型的RDD上使用,groupByKey只能作用在kv格式的RDD上
2.groupByKey之后的RDD结构相对简单一些
3.groupByKey需要传输的数据量更小,性能更高

 val groupByKeyRDD: RDD[(String, Iterable[Int])] = clazzAndAgeRDD.groupByKey()
 val avgAgeRDD: RDD[(String, Double)] = groupByKeyRDD.map {
      case (clazz: String, ages: Iterable[Int]) =>
        val avgAge: Double = ages.sum.toDouble / ages.size
        (clazz, avgAge)
    }
    /**
     * groupBy和groupByKey的区别
     * 1、代码:groupBy可以在任何类型的rdd上使用,groupByKey只能作用在kv格式的RDD上
     * 2、groupByKey之后RDD的结构相对简单一点
     * 3、性能:groupByKey shuffle过程需要传输的数据量比groupBy小,性能更高
     */
2.reducebykey
reduceByKey    比groupByKey性能更好
作用在kv格式的RDD      Scala中没有
按照key对value做聚合的函数
操作后也会产生shuffle,数据量比groupByKey少,

因为reduceByKey在map端做预聚合算一次局部结果,减少shuffle过程传输数据量,
但是有些复杂逻辑做不了,比如计算方差   

  //取出班级和年龄
    val clazzRDD: RDD[(String, Int)] =splitRDD.map{
      case Array(_,_,age:String,_,clazz:String)=>
        (clazz,1)
    }
    /**
     * reduceByKey按照key对值进行聚合,需要传聚合函数,会产生shuffle
     */
    val clazz_numRDD: RDD[(String, Int)] =clazzRDD.reduceByKey((x:Int, y:Int)=>x+y)
    clazz_numRDD.foreach(println)
3.union
union     类型一样的RDD进行合并,但不对数据进行去重,逻辑层面进行了合并但是物理层面没有合并,代码一起写但是RDD还是两个,新的RDD分区数等于两者的和
4.join
数据:       parallelize:根据一个kv格式的list转换构建成RDD
    val kvRDD1: RDD[(String, String)] =sc.parallelize(List(("001","张三"),("002","李四"),("003","王五")))
    val kvRDD2: RDD[(String, String)] =sc.parallelize(List(("001","张三"),("002","铁柱"),("003","姚欣"),("004","默认值")))
    val kvRDD3: RDD[(String, Int)] =sc.parallelize(List(("001",23),("002",24),("003",25),("006",26)))

1.inner join 内关联,两边对应的key都有数据 常用

join   需要kv格式的RDD,相同的key进行合并  提取出一个key
  val innerRDD: RDD[(String, (String, String))] =kvRDD1.join(kvRDD2)
  innerRDD.foreach(println)
  
(003,(王五,姚欣))
(002,(李四,铁柱))     //这个结果没有用map方法展开
(001,(张三,张三))

2.leftOuterJoin : 以左表为基础,如果右表没有数据补null 常用
option: 可选的值,可能没有关联上,两个取值,有值或者None,有值的在case时写 some(值)

    val leftjoin: RDD[(String, (String, Option[String]))] =kvRDD1.leftOuterJoin(kvRDD2)
    //分开处理数据
    leftjoin.map{
      case(id:String,(name:String,Some(age)))=>
        (id,name,age)
        case(id:String,(name:String,None))=>
          (id,name,1)
    }.foreach(println)

3.fullOuterJoin 一边有数据就会出结果,没有的补null,3种情况都写

val fulljoin: RDD[(String, (Option[Int], Option[String]))] =kvRDD3.fullOuterJoin(kvRDD2)
    fulljoin.map{
      case(id:String,(Some(name),Some(age)))=>
        (id,name,age)
      case(id:String,(Some(name),None))=>
        (id,name,1)
      case(id:String,(None,Some(age)))=>
        (id,1,age)
    }.foreach(println)
    
(003,25,姚欣)
(004,1,默认值)
(002,24,铁柱)
(006,26,1)
(001,23,张三)

读取数据得到(id,总分)形式的数据

5.sortBy
sortby:指定一个字段进行排序,默认升序,追加 ascending=false 为降序
需要有一个可以进行排序的数据
 //根据对key分组来求和    
    val sumsco: RDD[(String, Int)] =id_sco.reduceByKey((x:Int, y:Int)=>x+y)
    val sortRDD: RDD[(String, Int)] =sumsco.sortBy(kv=>kv._2,ascending=false)
6.mapvalues
key不变,只对value做处理
val sumsco: RDD[(String, Int)] =id_sco.reduceByKey((x:Int, y:Int)=>x+y)
val mapValues:RDD[(String,Int)]=sumsco.mapValues(scp=>scp/10)

以往方法:sumsco.map(kv=>(kv._1,kv._2/10))
7.mapPartitions
mapPartitions  
1.传入参数是String类型迭代器,在函数内对一个分区的数据进行处理,一次处理一个分区的数据
2.迭代器中是一个分区的数据,将分区内是数据一个个传给函数 
3.得到分区数:println(linesRDD.getNumPartitions)

 val mapRDD: RDD[String] = linesRDD.mapPartitions
    ((iter: Iterator[String]) => {
      //在函数类对一个分区的数据进行处理
      val words: Iterator[String] = iter.flatMap(line => line.split(","))
      words
    })
8.mapPartitionsWithIndex
mapPartitionsWithIndex       对应一个分区编号
 linesRDD.mapPartitionsWithIndex{
      case(index:Int,iter:Iterator[String])=>
        println(s"当前处理的分区编号:$index")
        iter
    }.foreach(println)  

//当前处理的分区编号:2
java,hadoop,hive
java,hadoop,hive
java,hadoop,hive 

3.Actions

转换算子是将一个RDD转换成另一个RDD,转换算子是懒执行,需要一个action算子来触发执行
一个操作算子用来触发一次任务执行,同时每一个操作算子都会出发前面代码执行,结果不是RDD,具体结果具体分析

3.1 froeach 打印数据
打印两次,没有操作算子的话转换算子会失效         
 val sc=new SparkContext(conf)
    val lineRDD: RDD[String] =sc.textFile("data/students.txt")
    val jieguo: RDD[(String, String)] =lineRDD.map(_.split(",")).map{
      case Array(id:String,_,_,_,clazz:String)=>
        (id,clazz)
    }
    jieguo.foreach(println)
    jieguo.foreach(println)
3.2 saveAsTextFile 保持数据
val studnetRDD: RDD[(String, String, Int, String, String)] = linesRDD
      .map(_.split(","))
      .map {
        case Array(id: String, name: String, age: String, gender: String, clazz: String) =>
          println("=======map============")
          (id, name, age.toInt, gender, clazz)
      }
       //保持数据
    studnetRDD.saveAsTextFile("data/temp")

    //统计RDD行数  
    val count: Long = studnetRDD.count()
    //将RDD转集合
    val studentArr: Array[(String, String, Int, String, String)] = studnetRDD.collect()
    //取top
    val top: Array[(String, String, Int, String, String)] = studnetRDD.take(100)
3.3 count 统计行数
3.4 collect 将RDD转换成集合
3.5 take 取top
3.6 sum RDD必须可以求和(数字类型)
3.7 reduce 全局聚合

4.spark缓存

1. 如何让RDD中有数据? cache
在spark中对一个转换RDD多次使用,需要对这个RDD计算多次,因为RDD默认不保存数据

需要缓存的RDD进行:xxxRDD.cache()    cache()默认的缓存级别是MEMORY_ONLY  保存在内存中不做压缩
当第一个任务执行的过程中会将数据缓存起来,后面执行的时候直接从缓存中获取数据,不需要重复进行计算了
2.缓存的级别和原则(缓存后存在哪里?)
RDD缓存的级别:磁盘,内存,堆外内存,序列化(是否压缩),副本(数量)
原则:1.如果RDD的数据量超过了内存限制,使用MEMORY_ONLY会导致内存溢出,值适合数据量不大内存充足的情况
2.如果将RDD的数据缓存在磁盘上,大部分情况和重新计算一次RDD的数据没有区别,重新读也是读磁盘,除了on disk之前还有很多逻辑
3.如果数据量比较大内存不是很够,可选择MEMORY_AND_DISK_SER,内存放不下再放磁盘,同时会对数据做压缩cpu执行时间换存储空间)      
4.对同一个RDD多次使用时才执行缓存策略
5.索引:空间换时间
xxxRDD.persist(StorageLevel.MEMORY_AND_DISK_SER)

在这里插入图片描述

3.checkpoint 和 cache
checkpoint
将RDD的数据缓存到hdfs,任务失败了不会丢失
通过sc提前设置保存路径     sc.setCheckpointDir("data/checkpoint")
主要在spark streaming中使用,保证任务高可用

cache 
将数据缓存在spark执行的服务器的内存或者磁盘上,任务失败数据就丢失
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值