Spark-core部分知识点

Spark

WordCount

package com.bigdata

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

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

    //创建上下文对象
    val conf = new SparkConf().setMaster("local[*]").setAppName("WordCount")
    val sc = new SparkContext(conf)

    //读取数据
    val inputRDD = sc.textFile("hdfs://192.168.40.131:9000/datas/wc.txt")

    //分析数据
    val resultRDD = inputRDD.flatMap(_.split(" "))
      .map((_, 1))
      .reduceByKey(_ + _)

    //数据存储
    resultRDD.foreach(println)
    resultRDD.saveAsTextFile("hdfs://192.168.40.131:9000/out_datas/")
  }
}

  1. 创建上下文对象
  2. 数据处理
    1. 读取数据
    2. 分析数据
    3. 存储数据

排序

  1. sortByKey(),参数:ascending :true 升序,false 降序
  2. sortby(),参数 1:指定排序字段,参数 2:ascending :true 升序,false 降序
  3. top(n):默认降序,参数:取前n个

案例:

package com.bigdata

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

object SparkTopKey {
  def main(args: Array[String]): Unit = {
    //创建上下文对象
    val conf = new SparkConf().setMaster("local[*]").setAppName("SparkTopKey")
    val sc = new SparkContext(conf)

    //读取数据
    val inputRdd = sc.textFile("hdfs://192.168.40.131:9000/datas/wc.txt")

    //分析数据,将数据进行排序(降序)
    val resultRDD = inputRdd.flatMap(_.split(" "))
      .map((_, 1))
      .reduceByKey(_ + _)
      .sortBy(_._2, false)
      .take(3)
      
    //打印
    resultRDD.foreach(println)

  }
}

Spark应用提交

#spark/bin/目录下
spark-submit

# Usage: spark-submit [options] <app jar | python file> [app arguments]

options参数:

  1. 基本参数

在这里插入图片描述

  1. Driver Program 相关参数

在这里插入图片描述

  1. Executor 相关参数

在这里插入图片描述

案例:

package bigdata

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

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

    if (args.length < 2){
      println("Usage: SparkSubmit <input> <output> ......")
      System.exit(1)
    }

    //创建上下文对象
    val conf = new SparkConf().setAppName("WordCount")
    val sc = new SparkContext(conf)

    //读取数据
    val inputRDD = sc.textFile(args(0))

    //分析数据
    val resultRDD = inputRDD.flatMap(_.split(" "))
      .map((_, 1))
      .reduceByKey(_ + _)

    //数据存储
    resultRDD.foreach(println)
    resultRDD.saveAsTextFile(args(1))
  }
}
  1. 命令行指定master
  2. 命令行指定输入输出路径

提交代码:

spark-submit \
--master local[2] \
--class bigdata.SparkSubmit \
file:///root/spark_jars/spark_scala-1.0-SNAPSHOT.jar \
hdfs://192.168.40.131:9000/user/spark/datas/WordCount.txt hdfs://192.168.40.131:9000/user/spark/output_data/
  1. –master :指定master
  2. –class :第一个参数:指定全类名,第二个参数:指定jar路径,第三个参数:输入路径,第四个参数:输出路径

注意:当jar在hdfs上时,启用本地模式可能存在错误

CORE

  1. 弹性数据集:RDD
  2. 共享变量:Shared Variables
    1. 累加器
    2. 广播变量

RDD创建

  1. 本地集合创建(Scala中集合对象)数据存储到RDD中(主要用与测试)

  2. 本地文件系统数据

    如:HDFS、LocalFS、kafak等

本地集合创建代码:

package bigdata

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

/**
  * Spark 采用并行化的方式构建Scala集合Seq中的数据为RDD
  */

object RDD_establish {

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

    //构建SparkContext对象,(模板)
    val sc:SparkContext = {
      // 创建SparkConf对象
      val sparkconf = new SparkConf()
        .setMaster("local[*]")
        .setAppName(this.getClass.getSimpleName.stripSuffix("$"))
      // 传递SparkConf对象,创建实例
      val context = SparkContext.getOrCreate(sparkconf) //有就选取,没有就创建
      // 返回实例对象
      context
    }

    //TODO:创建本地集合
    val seq = Seq(1,2,3,4,5,6,7,8)
    //并行化本地集合,创建RDD
    //两种方式,效果相同,makeRDD底层调用parallelize实现
    val RDD: RDD[Int] = sc.makeRDD(seq,2)
    
    val rdd1 = sc.parallelize(seq,2)
    
    sc.stop()
  }

}

外部存储系统创建代码:

package bigdata

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

object RDD_establish {

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

    //构建SparkContext对象
    val sc:SparkContext = {
      // 创建SparkConf对象
      val sparkconf = new SparkConf()
        .setMaster("local[*]")
        .setAppName(this.getClass.getSimpleName.stripSuffix("$"))
      // 传递SparkConf对象,创建实例
      val context = SparkContext.getOrCreate(sparkconf) //有就选取,没有就创建
      // 返回实例对象
      context
    }

	//外部存储创建,第一个参数:文件路径,第二个:分区数
	//可以指定文件名称,可以指定文件目录,可以使用通配符
    val rdd = sc.textFile("",2)
    
    //读取小文件数据
    //返回K-V形式数据,K:文件路径 V:数据
    /**
      * Read a directory of text files from HDFS, a local file system (available on all nodes), or any
      * Hadoop-supported file system URI. Each file is read as a single record and returned in a
      * key-value pair, where the key is the path of each file, the value is the content of each file.
      *
      * <p> For example, if you have the following files:
      * {{{
      *   hdfs://a-hdfs-path/part-00000
      *   hdfs://a-hdfs-path/part-00001
      *   ...
      *   hdfs://a-hdfs-path/part-nnnnn
      * }}}
      *
      * Do `val rdd = sparkContext.wholeTextFile("hdfs://a-hdfs-path")`,
      *
      * <p> then `rdd` contains
      * {{{
      *   (a-hdfs-path/part-00000, its content)
      *   (a-hdfs-path/part-00001, its content)
      *   ...
      *   (a-hdfs-path/part-nnnnn, its content)
      * }}}
      */
    val rdd2: RDD[(String, String)] = sc.wholeTextFiles("", 2)

    sc.stop()
  }

}

分区数目

获取分区的两种方式:

  1. rdd.getNumPartitions
  2. rdd.partitions.length

注:1. 从 HDFS 上加载海量数据时,RDD分区数目为block数目

		2. 从HBase表加载数据,RDD分区数目为Region数目

RDD函数

在这里插入图片描述

1. Transformation

注:所有Transformation函数都是懒惰的,不会立刻执行,需要Action函数触发

算子:

在这里插入图片描述

map(func)

:通过将函数应用于此RDD的所有元素来返回新RDD

//创建数据集
    val rdd = sc.makeRDD(List(1,2,3,4,5,6,7,8),2)

    //将集合中的元素乘2
    //map
    rdd.map(_*2).foreach(println)
filter(func)

:返回判断条件为true的元素

//创建数据集
    val rdd = sc.makeRDD(List(1,2,3,4,5,6,7,8),2)
//filter 偶数
    rdd.filter(_ % 2 == 0).foreach(println)
flatmap(func)

:首先将函数应用于所有元素,再将结果展平,返回新的RDD

//创建数据集
    val rdd = sc.makeRDD(List("hello spark","hello hadoop","with you"))
//flatmap
    rdd.flatMap(_.split(" ")).foreach(println)
mapPartitions(func)

:将函数独立应用在每一个分区内的元素,返回一个新的RDD(函数类型必须是:Iterator[T] => Iterator[U])

//创建数据集
    val rdd = sc.makeRDD(List(1,2,3,4,5,6,7,8),2)
//mapPartitions
    rdd.mapPartitions(
      iter => iter.map(_*2)
    ).foreach(println)
mapPartitionsWithIndex(func)

:功能与mapPartitions相似,但func多了一个参数表示分区索引,因此func类型必须是:(Int, Interator[T]) => Iterator[U]

//创建数据集
    val rdd = sc.makeRDD(List(1,2,3,4,5,6,7,8),2)
//mapPartitionsWithIndex 原数据加上分区号,返回字符串形式
    rdd.mapPartitionsWithIndex(
      (index, iter) => {
        iter.map(_ +" 分区号:"+ index)
      }
    ).foreach(println)
groupBy(func)

:按照函数的返回值进行分组,将相同key对应的值放到一个迭代器中,返回值类型为: RDD[(K, Iterable[T])]

sortBy(func)

:按照函数返回值进行排序,默认升序,ascending:true为升序,false为降序

sample

:抽样,参数 1. withReplacement:true数据又放回的抽取,false不放回抽取;2. fraction:随机抽样出数量;3. seed:指定随机数生 成器种子

randomSplit

:按照指定权重抽样,参数1:权重[Array]集合,总和需为1;参数2:随机数生成种子

unoin

:返回此RDD和另一个的并集,包含两个集合的所有元素

intersection

:返回两个RDD的交集,无重复元素

subtract

:RDD1.subtract(RDD2),返回RDD1独有的元素

distinct

:去除RDD中重复的元素

cartesian

:笛卡尔积

zip

:将两个RDD一一对应的组合成K-V形式的RDD,这里两个RDD的分区及元素数量都要相同,否则报错

coalesce

:缩减分区,不产生shuffle

repartition

:增加分区,产生shuffle

mapValue

:通过将函数应用于K-V类型RDD的Value元素,返回新的RDD

flatmapValue

:首先将函数应用于K-V类型RDD的Value元素,再将结果展平,返回新的RDD

groupByKey

:按K-V类型RDD的K进行分组,将相同Key对应的值放到一个Seq中,返回新的RDD

redyceBykey

:在一个(K,V)的 RDD 上调用,返回一个(K,V)的 RDD,使用指定的 reduce 函数,将相同 key 的值聚合到一起,reduce 任务的个数可以通过第二个可选的参数来设置

foldByKey

:与reduceByKey相似,多了一个参数可以指定初始值

aggregateByKey

:参数:(zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U)

:指定分区内计算规则,再指定分区间计算规则

sortByKey

:按照K进行排序

combineByKey

:与aggreateByKey相似,第一个参数不同,第一个参数将数据的第一个值进行结构转换作为初始值

join

:在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素对在 一起的(K,(V,W))的 RDD

cogroup

:在类型为(K,V)和(K,W)的 RDD 上调用,返回一个(K,(Iterable,Iterable))类 型的 RDD

partitionBy:

:对RDD进行重新分区,默认使用new org.apache.spark.HashPartitioner(2)分区规则

2. Action

注:所有Action函数立即执行

算子:

在这里插入图片描述

reduce

:对RDD内所有元素进行聚合,先聚合分区内,再聚合分区间

collect

:以数组的形式返回数据集的所有元素

aggregate

:与aggregateByKey相似

fold

:与foldByKey相似

first

:取出RDD中第一个元素

take

:取出指定数量的RDD的前N个元素

forEach

:在数据集的每一个元素上,运行函数 func 进行更新。

takeOrdered(n)

:返回该 RDD 排序后的前 n 个元素组成的数组

collectAsMap

:将K-V类型的RDD转换为Map

count

:返回 RDD 中元素的个数

max、min、sum、mean、stdev:

:最大值、最小值、总和、平均值、标准差

Keys、values

:获取K-V类型RDD的所有K值、获取K-V类型RDD的所有V值

countByKey

:统计K-V类型RDD所有的K出现的次数,返回collections.map类型

countByValue

:统计RDD中元素出现的次数,底层将数据转换为value =>(value,null)的形式再调用countByKey,返回collections.map类型

saveAsTextFile

:将数据集的元素以 textfile 的形式保存到 HDFS 文件系统或者其他支持的文件系统, 对于每个元素,Spark 将会调用 toString 方法,将它装换为文件中的文本

saveAsSequenceFile(path)

:将数据集中的元素以 Hadoop sequencefile 的格式保存到指定的目录下,可以使 HDFS 或者其他 Hadoop 支持的文件系统。

saveAsObjectFile(path)

:用于将 RDD 中的元素序列化成对象,存储到文件中。

自定义分区

要实现自定义的分区器,你需要继承 org.apache.spark.Partitioner 类并实现下面三个 方法。

(1)numPartitions: Int:返回创建出来的分区数。

(2)getPartition(key: Any): Int:返回给定键的分区编号(0 到 numPartitions-1)。

(3)equals():Java 判断相等性的标准方法。这个方法的实现非常重要,Spark 需要用这个 方法来检查你的分区器对象是否和其他分区器实例相同,这样 Spark 才可以判断两个 RDD 的分区方式是否相同。

数据读取和保存

  1. Text文件
    1. 读取:textFile
    2. 保存:saveAsTextFile
  2. json文件
    1. 导入:import scala.util.parsing.json.JSON
    2. 读取文件:val json = textFile
    3. 解析:json.map(JSON.parseFull)
  3. Sequence文件
    1. 读取:sc.sequenceFile[Int,Int]("file:///opt/module/spark/seqFile")
    2. 保存:saveAsSequenceFile("file:///opt/module/spark/seqFile")
  4. 对象文件
    1. 读取:sc.objectFile[(Int)]("file:///opt/module/spark/objectFile")
    2. 保存:saveAsObjectFile("file:///opt/module/spark/objectFile")

文件系统数据读取

  1. Mysql

    支持通过 Java JDBC 访问关系型数据库。需要通过 JdbcRDD 进行,示例如下:

    (1)添加依赖

    <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.27</version>
    </dependency>
    

    (2)Mysql 读取:

    package com.atguigu
    import java.sql.DriverManager
    import org.apache.spark.rdd.JdbcRDD
    import org.apache.spark.{SparkConf, SparkContext}
    
    object MysqlRDD {
    	def main(args: Array[String]): Unit = {
     		//1.创建 spark 配置信息
     		val sparkConf: SparkConf = new
    			SparkConf().setMaster("local[*]").setAppName("JdbcRDD")
     		//2.创建 SparkContext
     		val sc = new SparkContext(sparkConf)
     		//3.定义连接 mysql 的参数
     		val driver = "com.mysql.jdbc.Driver"
     		val url = "jdbc:mysql://hadoop102:3306/rdd"
     		val userName = "root"
     		val passWd = "000000"
     		//创建 JdbcRDD
     		val rdd = new JdbcRDD(sc, () => {
     			Class.forName(driver)
     			DriverManager.getConnection(url, userName, passWd)
     			},
     			"select * from `rddtable` where `id`>=?;",
     			1,
     			10,
     			1,
     			r => (r.getInt(1), r.getString(2))
     		)
            //打印最后结果
     		println(rdd.count())
     		rdd.foreach(println)
     		sc.stop()
    	}
    }
    
    

    Mysql 写入:

    def main(args: Array[String]) {
     val sparkConf = new 			SparkConf().setMaster("local[2]").setAppName("HBaseApp")
     val sc = new SparkContext(sparkConf)
     val data = sc.parallelize(List("Female", "Male","Female"))
     data.foreachPartition(insertData)
    }
    def insertData(iterator: Iterator[String]): Unit = {
    Class.forName ("com.mysql.jdbc.Driver").newInstance()
     val conn = java.sql.DriverManager.getConnection("jdbc:mysql://master01:3306/rdd", "root",
    "hive")
     iterator.foreach(data => {
     val ps = conn.prepareStatement("insert into rddtable(name) values (?)")
     ps.setString(1, data)
     ps.executeUpdate()
     })
    }
    
    

累加器

  1. 获取系统累加器

    val sumAcc = sc.longAccumulator(name="sum")

  2. 使用累加器

    sumAcc.add(num)

  3. 获取累加器结果

    sumAcc.value

自定义累加器

package bigdata

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

import scala.collection.mutable

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

    val sc:SparkContext = {
      val conf:SparkConf = new SparkConf()
        .setMaster("local[*]")
        .setAppName(this.getClass.getSimpleName.stripSuffix("$"))

      SparkContext.getOrCreate(conf)
    }
    val rdd = sc.makeRDD(List("hello","word","spark","hello"))

    //创建累加器对象
    val wcAcc = new MyAccumulator()

    //向Spark进行注册
    sc.register(wcAcc, "wordCountAcc")

    rdd.foreach(
      word => {
        wcAcc.add(word)
      }
    )

    //获取累加器结果
    println(wcAcc.value)
  }

  /**
    * 自定义累加器
    * IN:累加器输入的数据类型
    * OUT:累加器返回的数据类型
    */
  class MyAccumulator extends AccumulatorV2[String,mutable.Map[String,Long]]{

    private var wcMap = mutable.Map[String,Long]()

    //判断是否为初始状态
    override def isZero: Boolean = {
      wcMap.isEmpty
    }

    //赋值累加器
    override def copy(): AccumulatorV2[String, mutable.Map[String, Long]] = {
      new MyAccumulator()
    }

    //重置累加器
    override def reset(): Unit = {
      wcMap.clear()
    }

    //获取累加器需要计算的值
    override def add(word: String): Unit = {
      val newCnt = wcMap.getOrElse(word,0L) + 1
      wcMap.update(word,newCnt)
    }

    //合并多个累加器
    override def merge(other: AccumulatorV2[String, mutable.Map[String, Long]]): Unit = {
      val map1 = this.wcMap
      val map2 = other.value

      map2.foreach{
        case (word ,count) => {
          val newCount = map1.getOrElse(word,0L) + count
          map1.update(word,newCount)
        }
      }
    }

    //累加器结果
    override def value: mutable.Map[String, Long] = {
      wcMap
    }
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值