Spark基础及架构

为什么我们要使用Spark
1、MapReduce编程模型的局限性
繁杂:只有Map和Reduce两个操作,复杂的逻辑需要大量的样板代码
处理效率低:Map中间结果写进磁盘,Reduce写HDFS,多个Map通过HDFS交换数据;任务调度与启动开销大不适合迭代处理、交互式处理和流式处理
2、Spark是类Hadoop MapReduce的通用并行框架
Job中间输出结果可以保存在内存,不再需要读写HDFS;比MapReduce平均快10倍以上

Spark优势
1、速度快:基于内存数据处理,比MR快100个数量级以上(逻辑回归算法测试);基于硬盘数据处理,比MR快10个数量级以上
2、易用性:支持Java、Scala、Python、R语言;交互式shell方便开发测试
3、通用性:一栈式解决方案:批处理、交互式查询、实时流处理、图计算及机器学习
4、随处运行:YARN、Mesos、EC2、Kubernetes、Standalone、Local

Spark技术栈
Spark Core:核心组件,分布式计算引擎
Spark SQL:高性能的基于Hadoop的SQL解决方案
Spark Streaming:可以实现高吞吐量、具备容错机制的准实时流处理系统
Spark GraphX:分布式图处理框架
Spark MLlib:构建在Spark上的分布式机器学习库

Spark架构设计
运行架构
在驱动程序中,通过SparkContext主导应用的执行
SparkContext可以连接不同类型的Cluster Manager(Standalone、YARN、Mesos),连接后,获得集群节点上的Executor
一个Worker节点默认一个Executor,可通过SPARK_WORKER_INSTANCES调整
每个应用获取自己的Executor
每个Task处理一个RDD分区

Spark架构核心组件
在这里插入图片描述
Spark API-1
SparkContext
连接Driver与Spark Cluster(Workers)
Spark的主入口
每个JVM仅能有一个活跃的SparkContext
SparkContext.getOrCreate

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

val conf=new SparkConf().setMaster("local[2]").setAppName("HelloSpark")
val sc=SparkContext.getOrCreate(conf)

Spark API-2
SparkSession
Spark 2.0+应用程序的主入口:包含了SparkContext、SQLContext、HiveContext以及StreamingContext
SparkSession.getOrCreate

import org.apache.spark.sql.sqparkSession

val spark=SparkSession.builder
          .master("local[2]")
          .appName("appName")
          .getOrCreate()

需求说明
创建Maven项目,添加依赖
scala-library
spark-core
spark-sql
创建SparkContext
创建SparkSession
使用Spark实现WordCount

//添加依赖
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>2.11.8</version>
    </dependency>
    <dependency>
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-core_2.11</artifactId>
      <version>2.1.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-sql_2.11</artifactId>
      <version>2.1.1</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.21</version>
    </dependency>
//实现代码
import org.apache.spark.{Partition, SparkConf, SparkContext}

object WordCount {
  def main(args: Array[String]): Unit = {
    //setAppName("worldcount")给Application命名
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("worldcount")
    val sc: SparkContext = SparkContext.getOrCreate(conf)

    rdd1.flatMap(_.split("\t")).map(x=>(x, 1)).reduceByKey(_ + _).collect.foreach(println)
    // (hello scala,1)
    // (hello java,1)
    // (hello world,1)
    val patitions:Array[Partition]=rdd1.partitions
    println("rdd分区数:"+patitions.length)
    //rdd分区数:5
  }
}

Spark API-3
RDD:Spark核心,主要数据抽象
Dataset:从Spark1.6开始引入的新的抽象,特定领域对象中的强类型集合,它可以使用函数或者相关操作并行地进行转换等操作
DataFrame:DataFrame是特殊的Dataset

Spark RDD概念-1
简单的解释:RDD是将数据项拆分为多个分区的集合,存储在集群的工作节点上的内存中,并执行正确的操作
复杂的解释:RDD是用于数据转换的接口;RDD指向了存储在HDFS、Cassandra、HBase等、或缓存(内存、内存+磁盘、仅磁盘等),或在故障或缓存收回时重新计算其他RDD分区中的数据

Spark RDD概念-2
RDD是弹性分布式数据集(Resilient Distributed Datasets)
分布式数据集:RDD是只读的、分区记录的集合,每个分区分布在集群的不同节点上;RDD并不存储真正的数据,只是对数据和操作的描述
弹性:RDD默认存放在内存中,当内存不足,Spark自动将RDD写入磁盘
容错性:根据数据血统,可以自动从节点失败中恢复分区

RDD的创建-1
使用集合创建RDD

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

object WordCount {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("worldcount")
    val sc: SparkContext = SparkContext.getOrCreate(conf)
    val rdd=sc.parallelize(List(1,2,3,4,5,6))
    rdd.count
    rdd.partitions.size //2
    val rdd=sc.parallelize(List(1,2,3,4,5,6),5)
    rdd.partitions.size //5
    val rdd=sc.makeRDD(List(1,2,3,4,5,6))
    // 1、Spark默认会根据集群的情况来设置分区的数量,也可以通过parallelize()第二参数来指定
    // 2、Spark会为每一个分区运行一个任务进行处理
  }
}

RDD的创建-2
通过加载文件产生RDD

// 通过本地加载
//加载“file://……”时,以local运行仅需一份本地文件,以Spark集群方式运行,应保证每个节点均有该文件的本地副本
val distFile=sc.tectFile("file:///opt/hadoop/data/word.txt")
//通过hdfs加载
val distHDFSFile=sc.textFile("hdfs://hadoop1:9000/kb09workspace/word.txt")

//支持目录、压缩文件以及通配符
// 1、Spark默认访问HDFS
// 2、Spark默认为HDFS文件的每一个数据块创建一个分区,也可以通过textFile()第二个参数指定,但只能比数据块数量多
sc.textFile("/kb09workspace/")
sc.textFile("/kb09workspace/*.txt")
sc.textFile("/kb09workspace/*.gz")

RDD分区
分区是RDD被拆分并发送到节点的不同块之一
我们拥有的分区越多,得到的并行性就越强
每个分区都是被分发到不同Worker Node的候选者
每个分区对应一个Task

RDD转换算子
对于转换操作,RDD的所有转换都不会直接计算结果
仅记录作用于RDD上的操作
当遇到动作算子(Action)时才会进行真正计算

在这里插入图片描述

RDD常用转换算子
map算子
对RDD中的每个元素都执行一个指定的函数来产生一个新的RDD
任何原RDD中的元素在新RDD中都有且只有一个元素与之对应
输入分区与输出分区一一对应

//将原RDD中每个元素都乘以2来产生一个新的RDD
val a=sc.parallelize(1 to 9)
val b=a.map(x=>x*2)
a.collect
b.collect

//map把普通RDD变成PairRDD
val a=sc.parallelize(List("dog","tiger","lion","cat","panther","eagle"))
val b=a.map(x=>(x,1))
b.collect

filter算子
对元素进行过滤,对每个元素应用指定函数,返回值为true的元素保留在新的RDD中

val a=sc.parallelize(1 to 10)
a.filter(_%2==0).collect      
a.filter(_<4).collect	

//map&filter
val rdd=sc.parallelize(List(1 to 6))
val mapRdd=rdd.map(_*2)
mapRdd.collect
val filterRdd=mapRdd.filter(_>5)
filterRdd.collect

mapValues算子
原RDD中的Key保持不变,与新的Value一起组成新的RDD中的元素,仅适用于PairRDD

val a=sc.parallelize(List("dog","tiger","lion","cat","panther","eagle"))
val b=a.map(x=>(x.length,x))
b.mapValues("x"+_+"x").collect
//输出结果:Array((3,xdogx), (5,xtigerx), (4,xlionx), (3,xcatx), (7,xpantherx), (5,xeaglex))

更多常用转换算子
distinct、reduceByKey、groupByKey、sortByKey、union、join、count …

//去重
val dis = sc.parallelize(List(1,2,3,4,5,6,7,8,9,9,2,6))
dis.distinct.collect
dis.distinct(2).partitions.length

//通过map中key进行操作
val a = sc.parallelize(List("dog", "salmon", "pig"), 3)
val f = a.map(x=>(x.length,x))
f.reduceByKey((a,b)=>(a+b)).collect
f.reduceByKey(_+_).collect
f.groupByKey.collect
f.sortByKey().collect
f.sortByKey(false).collec

//union同++,联合
val u1 = sc.parallelize(1 to 3)
val u2 = sc.parallelize(3 to 4)
u1.union(u2).collect
(u1 ++ u2).collect
u1.intersection(u2).collect

//类似sql中join,leftOuterjoin,rightOuterJoin
val j1 = sc.parallelize(List("abe", "abby", "apple")).map(a => (a, 1))                                                                 
val j2 = sc.parallelize(List("apple", "beatty", "beatrice")).map(a => (a, 1))
j1.join(j2).collect
j1.leftOuterJoin(j2).collect
j1.rightOuterJoin(j2).collect

RDD动作算子
本质上动作算子通过SparkContext执行提交作业操作,触发RDD DAG(有向无环图)的执行
所有的动作算子都是急迫型(non-lazy),RDD遇到Action就会立即计算

RDD常用动作算子
count
返回集合中元素个数

val rdd=sc.parallelize(List(1,2,3,4,5,6))
println(rdd.count) // 6

collect
以Array返回RDD的所有元素。一般在过滤或者处理足够小的结果的时候使用

val rdd=sc.parallelize(List(1,2,3,4,5,6))
rdd.collect // Array(1,2,3,4,5,6)

take
返回前n个元素

val rdd=sc.parallelize(List(1,2,3,4,5,6))
rdd.take(3) // List(1,2,3)

first
返回RDD第一个元素

val rdd=sc.parallelize(List(1,2,3,4,5,6))
rdd.first // 1

reduce
根据指定函数,对RDD中的元素进行两两计算,返回计算结果

val rdd=sc.parallelize(List(1,2,3,4,5,6))
rdd.reduce(_+_) // 21
rdd.reduce((x,y)=>x+y) // 21
val b=sc.parallelize(Array(("A",0), ("A",2), ("B",1), ("B",2), ("C",1)))
b.reduce((x,y)=>{(x._1+y._1,x._2+y._2)}) // (AABBC,6)

foreach
对RDD中每个元素都使用指定函数,无返回值

val rdd=sc.parallelize(List(1,2,3,4,5,6))
rdd.foreach(println) // 1,2,3,4,5,6

lookup
用于PairRDD,返回K对应的所有V值

val rdd=sc.parallelize(List(('a',1),('b',2),('c',3),('a',4)))
rdd.lookup('a') // WrapperedArray(1,4)

max,min
返回最大值最小值

val rdd=sc.parallelize(10 to 30)
rdd.max // 30
rdd.min // 10

saveAsTextFile
保存RDD数据至文件系统

val rdd=sc.parallelize(1 to 10,2)
rdd.saveAsTextFile("hdfs://kb09workspace/data/rddsave/")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值