Spark基础及架构

一、为什么使用Spark

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

二、Spark优势

  • 速度快
    基于内存数据处理,比MR快100个数量级以上(逻辑回归算法测试)
    基于硬盘数据处理,比MR快10个数量级以上
  • 易用性
    支持Java、Scala、Python、R语言
    交互式shell方便开发测试
  • 通用性
    一栈式解决方案:批处理、交互式查询、实时流处理、图计算及机器学习
  • 随处运行
    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架构核心组件

术语说明
Application建立在Spark上的用户程序,包括Driver代码和运行在集群各节点Executor中的代码
Driver program驱动程序。Application中的main函数并创建SparkContext
Cluster Manager在集群(Standalone、Mesos、YARN)上获取资源的外部服务
Worker Node集群中任何可以运行Application代码的节点
Executor某个Application运行在worker节点上的一个进程
Task被送到某个Executor上的工作单元
Job包含多个Task组成的并行计算,往往由Spark Action触发生成,一个Application中往往会产生多个Job
Stage每个Job会被拆分成多组Task,作为一个TaskSet,其名称为Stage

六、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("wordCount")
val sc = SparkContext.getOrCreate(conf)
2.SparkSession
  • Spark 2.0+应用程序的主入口:包含了SparkContext、SQLContext、HiveContext以及StreamingContext
  • SparkSession.getOrCreate
import org.apache.spark.sql.SparkSession
val spark = SparkSession.builder
                    .master("local[2]")
                    .appName("appName")
                    .getOrCreate()

3.Spark RDD
  • RDD
    Spark核心,主要数据抽象
  • Dataset
    从Spark1.6开始引入的新的抽象,特定领域对象中的强类型集合,它可以使用函数或者相关操作并行地进行转换等操作
  • DataFrame
    DataFrame是特殊的Dataset
4.Spark RDD概念
  1. SparkRdd的解释
  • 简单的解释
    RDD是将数据项拆分为多个分区的集合,存储在集群的工作节点上的内存中,并执行正确的操作
  • 复杂的解释
    RDD是用于数据转换的接口
    RDD指向了存储在HDFS、Cassandra、HBase等、或缓存(内存、内存+磁盘、仅磁盘等),或在故障或缓存收回时重新计算其他RDD分区中的数据
  1. RDD是弹性分布式数据集(Resilient Distributed Datasets)
  • 分布式数据集
    RDD是只读的、分区记录的集合,每个分区分布在集群的不同节点上;
    RDD并不存储真正的数据,只是对数据和操作的描述
  • 弹性
    RDD默认存放在内存中,当内存不足,Spark自动将RDD写入磁盘
  • 容错性
    根据数据血统,可以自动从节点失败中恢复分区
  1. RDD与DAG
  • 两者是Spark提供的核心抽象
  • DAG(有向无环图)反映了RDD之间的依赖关系
    在这里插入图片描述
  1. RDD的特性
  • 一系列的分区(分片)信息,每个任务处理一个分区
  • 每个分区上都有compute函数,计算该分区中的数据
  • RDD之间有一系列的依赖
  • 分区函数决定数据(key-value)分配至哪个分区
  • 最佳位置列表,将计算任务分派到其所在处理数据块的存储位置
  1. RDD编程流程
    在这里插入图片描述

七、Spark RDD的创建

  • 使用集合创建RDD
//使用默认分区数量
val rdd = sc.parallelize(List(1,2,3,4,5))
rdd.count
val partition1 = rdd.partitions.size
println(partition1)

//通过parallelize的第二个参数指定分区数量
val rdd = sc.parallelize(List(1,2,3,4,5),5)
val partition2 = rdd.partitions.size
println(partition2)

//使用makeRDD创建,makeRDD基于parallelize基础,便于后期优化,推荐使用
val rdd = sc.makeRDD(List(1,2,3,4,5))
  • 通过加载文件产生RDD
//绝对路径加载
val text1 = sc.textFile("文件绝对路径")
text1.collect.foreach(println)

//相对路径加载
val text2 = sc.textFile("文件相对路径")
text2.collect.foreach(println)

//文件上传hdfs后,通过hdfs加载
val text3 = sc.textFile("hdfs://hadoop001:9000/hdfs文件路径")
text3.collect.foreach(println)

注:textFile支持目录,压缩文件及通配符

  • RDD创建方式
    1) 测试环境
    使用内存集合创建RDD makeRDD
    使用本地文件创建RDD textFile
    2)生产环境
    使用HDFS文件创建RDD

八、Spark RDD分区

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

在这里插入图片描述

九、Spark RDD的操作

  • 分为lazy与non-lazy两种
    Transformation(lazy):也称转换操作、转换算子
    Actions(non-lazy):立即执行,也称动作操作、动作算子
1.RDD转换算子
  • 对于转换操作,RDD的所有转换都不会直接计算结果
    仅记录作用于RDD上的操作
    当遇到动作算子(Action)时才会进行真正计算

  • 常用的转换算子

  1. map算子
    1)对RDD中的每个元素都执行一个指定的函数来产生一个新的RDD
    2)任何原RDD中的元素在新RDD中都有且只有一个元素与之对应
    3)输入分区与输出分区一一对应
val a=sc.makeRDD(List(1,2,3))
a.map(x=>(_,1))
  1. filter算子
    对元素进行过滤,对每个元素应用指定函数,返回值为true的元素保留在新的RDD中
val a=sc.makeRDD(List(1 to 10))
val b=a.filter(_%2==0)
b.collect.foreach(println)
  1. mapValues算子
    原RDD中的Key保持不变,与新的Value一起组成新的RDD中的元素,仅适用于PairRDD
val mapValuesRdd1 = sc.parallelize(List("tiger","dog","lion","eagle","cat"))
val mapValuesRdd2 = mapValuesRdd1.map(x=>(x.size,x))
mapValuesRdd2.collect.foreach(println)
//对value进行操作
val mapValuesRdd3 = mapValuesRdd2.mapValues(x=>"_"+x+"_")
mapValuesRdd3.collect.foreach(println)
  1. distinct算子
    去重
val rdd1=sc.parallelize(List(1,1,5,7,5))
val rdd2 = rdd1.distinct
println("rdd1的分区数:"+rdd1.partitions.size)
println("rdd2的分区数:"+rdd2.partitions.size)
rdd2.collect.foreach(println)
//加入参数重新分区
val rdd3=rdd1.distinct(3)
println("rdd2的分区数:"+rdd3.partitions.size)
  1. union / ++
    并集
val a=sc.makeRDD(List(1,2,3,4))
val b=sc.makeRDD(List(4,5,6))
val u1=a.union(b)
val u2=a++b
  1. intersection
    交集
val a=sc.makeRDD(List(1,2,3,4))
val b=sc.makeRDD(List(4,5,6))
val u = a.intersection(b)
u.collection.foreach(println)
  1. groupByKey、reduceByKey、sortByKey
    分类、汇总、排序
val Rdd1 = sc.makeRDD(List("tiger","dog","lion","eagle","cat"))
val Rdd2 = Rdd1.map(x=>(x.size,x))
println("--------reduceByKey--------")
val reduceByKeyRdd1 = Rdd2.reduceByKey(_+_)
Rdd1.collect.foreach(println)
println("--------groupByKey--------")
val groupByKeyRdd = Rdd2.groupByKey()
groupByKeyRdd.collect.foreach(println)
println("---------sortByKey--------")
//正序,默认
val sortByKeyRdd = Rdd2.sortByKey()
sortByKeyRdd.collect.foreach(println)
//倒序
val sortByKeyRdd2 = Rdd2.sortByKey(false)
sortByKeyRdd2.collect.foreach(println)
2.RDD动作算子
  • 本质上动作算子通过SparkContext执行提交作业操作,触发RDD DAG(有向无环图)的执行
  • 所有的动作算子都是急迫型(non-lazy),RDD遇到Action就会立即计算
  1. count
    返回的是数据集中的元素的个数
val a=sc.makeRDD(List(1,2,3,4))
val u=a.count
println(u)
  1. collect
    以Array返回RDD的所有元素。一般在过滤或者处理足够小的结果的时候使用
val rdd=sc.parallelize(List(1,2,3,4,5,6))
rdd.collect
  1. take
    返回前n个元素
val rdd=sc.parallelize(List(1,2,3,4,5,6))
rdd.take(3)
  1. first
    返回RDD第一个元素
val rdd=sc.parallelize(List(1,2,3,4,5,6))
rdd.first
  1. reduce
    根据指定函数,对RDD中的元素进行两两计算,返回计算结果
val rdd1=sc.makeRDD(1 to 100)
val sum =rdd1.reduce((x,y)=>{println(x,y);x+y})
println("总和:"+sum)
  1. foreach
    对RDD中的每个元素都使用指定函数,无返回值
val rdd=sc.parallelize(1 to 100)
rdd.foreach(println)
  1. lookup
    用于PairRDD,返回K对应的所有V值
val rdd=sc.parallelize(List(('a',1), ('a',2), ('b',3), ('c',4)))
rdd.lookup('a')
  1. saveAsTextFile
    保存RDD数据至文件系统

保存本地

//本地路径
rdd1.saveAsTextFile("in/rdd1.txt")

上传hdfs

//hdfs路径
rdd1.saveAsTextFile("hdfs://hadoop001:9000/data/rdd1.txt")

注:如果报错则注意权限,赋权代码:hdfs dfs -chmod 777 /data

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值