Spark总述

目录

 

前言

一、Spark 基本定义

Spark 相对于 MapReduce 的优势

二、Spark 的组成

三、Spark 运作时架构

四、任务层定义

五、RDD间依赖关系:宽窄依赖(shuffle)

pom 文件

总结


 


前言

学会用一个技术只是第一步,最重要的是要追问自己:

  • 这个技术解决了哪些痛点?
  • 别的技术为什么不能解决?
  • 这个技术用怎样的方法解决问题?
  • 采用这个技术真的是最好的方法吗?
  • 如果不用这个技术,你会怎样独立解决这类问题?

 

一、Spark 基本定义

spark解决了什么问题? 处理大量的数据和计算时候。需要一台超级电脑,来做计算和保存。spark/map reduce ,就是提供了这样的一套系统,可以在多台机器上并行做计算,(每台机子做的工作都相同,相当于工作量都减轻为原来的1/n)多部电脑同时读写。单机处理问题,首先容量不够,同时计算能力不足。所以想的方法是将任务拆解给不同的机子,让他们同时做相同的事情,之后再将每个机子的结果进行整和。所以map reduce/spark 主要解决的就是数据计算问题

spark 是什么
  • Spark 是一个用来实现快速而通用的集群计算的平台。
  • Spark 的核心是一个计算引擎,作用是对Spark应用进行调度、分发以及监控的。
  • 一个Spark应用由很多计算任务组成的、运行在多个工作机器或者是一个计算集群上,由一个驱动器程序(driver program)来发起集群上的各种并行操作。
spark 解决了什么问题

当数据量太大时,spark解决了单机无法处理大量数据的计算问题

spark 如何解决

分而治之

将数据和计算任务分解,将计算逻辑分发到各个数据模块所在机子上,每个机子的任务相同,计算数据中的一部分,最后将各个机子的结果汇总,得到最终的结果

如果没有spark, 那么应该如何解决

场景:统计日志中出现的所有url及其出现的次数

单机版本解决方案:建立map ,key: url, value: 该URL出现的次数

遍历日志中的每一行,并找到url:
if url in map:
    map[url] += 1
else:
    map[url] = 1

问题:日志太大,单机内存不足

解决方案:将数据存在hdfs上

 

问题:完整日志被切分为多个子模块,存在hdfs中不同的机器上

解决方案:

  • 方案1:用单机逐个读取hdfs的数据块
  • 弊端;
    • 产生的中间结果,单机的内存是否足够保存
    • 单机的处理能力有限,任务处理速度慢
  • 方案2:将运算逻辑分发到各个数据模块所在的机子上。数据在哪,就在哪计算
  • 待解决的问题:
    • 业务逻辑(运算代码)应该分发在哪些机器上(理想情况,是把运算代码发到数据所在的机器上,如何找到这些机器?策略是?)
    • 如何将运算代码分发到各个机器上(资源分配,启动环境)
    • 每个机器负责输出一部分结果。如果其中一个机器出故障,输出的有误的结果会影响最终的结果。(监控哪些节点正常哪些不正常。不正常的话,如何处理)
    • 如何将各个机器的输出结果汇总

map reduce 是把以上的问题的解决方案全部封装好,并提供各种API, 程序员只需要会写业务逻辑就好。

  
  

 

Spark 相对于 MapReduce 的优势

高效性
  • MapReduce将中间计算结果放入磁盘中,而Spark采用内存存储中间计算结果,减少了迭代运算的磁盘IO。并且内存的读取速度比磁盘高几个数量级
  • Spark通过并行计算DAG图的优化,减少了不同任务之间的依赖,降低了延迟等待时间。
  • 内存计算下,Spark 比 MapReduce 快100倍。
易用性MapReduce 只支持Map 和Reduce 两种编程算子,Spark提供了超过80种不同的transformation和action算子。实现相同的功能,spark的代码量极大缩小
通用性

Spark提供了统一的解决方案。Spark可以用于批处理、交互式查询(Spark SQL)、实时流处理(Spark Streaming)、机器学习(Spark MLlib)和图计算(GraphX)。

这些不同类型的处理都可以在同一个应用中无缝使用。这对于企业应用来说,就可使用一个平台来进行不同的工程实现,减少了人力开发和平台部署成本。

兼容性Spark能够跟很多开源工程兼容使用。如Spark可以使用Hadoop的YARN和Apache Mesos作为它的资源管理和调度器,并且Spark可以读取多种数据源,如HDFS、HBase、MySQL等。

二、Spark 的组成

spark 组成部分作用

GraphX

图计算

是用来操作图(比如社交网络的朋友关系图)的程序库,可以进行并行的图计算。

与Spark Streaming 和Spark SQL 类似,GraphX 也扩展了Spark 的RDD API,能用来创建一个顶点和边都包含任意属性的有向图。GraphX 还支持针对图的各种操作

MLlib

机器学习

提供了很多种机器学习算法,包括分类、回归、聚类、协同过滤等,还提供了模型评估、数据导入等额外的支持功能。如果我们要调用一个randomforest函数,就需要调用MLlib接口

Spark Core

实现了Spark 的基本功能,包含任务调度、内存管理、错误恢复、与存储系统交互等模块。

Spark Core 提供了创建和操作这些集合的多个API (比如filter, map, reduce等)

Spark SQL

结构化数据

是Spark 用来操作结构化数据的程序包。通过Spark SQL,

我们可以使用SQL或者Apache Hive 版本的SQL 方言(HQL)来查询数据。Spark SQL 支持多种数据源,比如Hive 表、Parquet 以及JSON 等。

也就是说多亏了Spark SQL, 我们才可以定义sql =" xxx" sc.sql(sql) 关联到hive表中并且从中读取数据

Spark Streaming

实时计算

Spark 提供的对实时数据进行流式计算的组件。比如生产环境中的网页

服务器日志,或是网络服务中用户提交的状态更新组成的消息队列,都是数据流。Spark

Streaming 提供了用来操作数据流的API,并且与Spark Core 中的RDD API 高度对应。

集群管理器

Spark 设计为可以高效地在一个计算节点到数千个计算节点之间伸缩计算。为了实现这样的要求,同时获得最大灵活性,Spark 支持在各种集群管理器(cluster

manager)上运行,包括Hadoop YARN、Apache Mesos,以及Spark 自带的一个简易调度器,叫作独立调度器。

如果已经有了一个装有Hadoop YARN 或Mesos的集群,通过Spark 对这些集群管理器的支持,你的应用也同样能运行在这些集群上

三、Spark 运作时架构

每个spark 应用都由一个驱动器(driver program)来发起集群上的各种并行操作

核心概念

作用

worker 集群中的机器
  • 管理executor进程
  • executor 运行在worker上。worker上可以起多个spark application的executor。但是一个spark application在一个worker上只有一个executor

RDD

弹性分布式数据集

弹性分布式数据集,它是记录的只读分区集合,是Spark的基本数据结构。

RDD代表一个不可变、可分区、里面的元素可并行计算的集合。

RDD的操作有两种类型,即Transformation操作和Action操作。转换操作是从已经存在的RDD创建一个新的RDD,而行动操作是在RDD上进行计算后返回结果到 Driver。

Transformation操作都具有 Lazy 特性,即 Spark 不会立刻进行实际的计算,只会记录执行的轨迹,只有触发Action操作的时候,它才会根据 DAG 图真正执行。

对RDD操作,其实是对RDD的每个分区上data进行操作,task调度executor上data执行相关的计算逻辑,进而对数据进行操作。

master (spark 自带的集群管理器)

cluster manager (Yarn/Mesos 开源集群管理器)

  • 管理worker node,监控worker node 的存活情况
  • 接受spark application请求
  • 资源调度(决定在哪些worker上起executor进程)

executor

执行器

  • 真正执行计算task的地方 task是在driver端生成,executor处执行
  • 任务在执行器程序中进行计算并保存结果,数据执行cache的时候,是存储在executor端

driver program

驱动器程序

  • 负责中央协调,调度各个分布式执行器(executor)
  • 提交spark application, 和master/cluster manager 交互来申请资源
  • 定义计算逻辑图DAG,定义job→ stage→ task (根据程序中所定义的对RDD 的转化操作和行动操作,driver将job拆分成多个stage,每个stage执行一部分代码片段,并为每个stage创建一批tasks,然后将这些tasks通过网络分配到各个executor中执行)
  • 将task分配到exeutor上,管理executor

DAG

有向无环图

反映RDD之间的依赖关系, 由driver创建

一个action对应一个job, 对应一个DAG

spark执行任务一览

  1. 集群准备 先起master再起worker,worker和master进行通讯,向master注册自己,把自己的内存和核数(机器的默认配置)都汇报给maste,这样master就知道哪些worker可用,而且知道每个worker的内存和核数,方便之后的资源调度。master给worker返回信息以后,worker就会定期给master发心跳,为了证明自己还活着。
  2. Spark-submit/Driver是客户端。客户端就是一个应用程序,支持和服务器的协议,实现和相应服务器间的交互比如微信,我们要使用微信,接受微信上的数据,那就需要先登陆客户端(微信的客户端可以是手机APP,电脑软件,还有电脑的网页)只有登陆上客户端以后,才能访问腾讯微信的服务器,接受聊天数据啊发聊天信息啊啥的。然后各个服务程序的客户端和服务器是一一匹配的,比如淘宝APP(淘宝客户端)就不能和微信的服务器相互交互。总而言之,要使用某种服务,要使用相应的服务器,必须要先登陆相应的客户端。
    用户
    1. 编写程序,定义一个class/object对象,其中编写main 函数,main() 中创建SparkContext代表对Spark集群的链接,并定义对RDD的各种创建转化和修改等操作。
    2. 打包代码和依赖:
      1. 如果只有一两个库的依赖且这些库本身不依赖于其他库时,通过spark-submit 的--jars 标记提交独立的JAR 包依赖
      2. 若依赖很多库,使用构建工具Maven,在pom.xml中添加所有需要的库(必不可少的是引入spark.core) 然后将整个程序成单个大JAR 包,包含应用的所有的传递依赖。
      3. 编写spark-submit 脚本,其中定义要连接的集群管理器Yarn,上传Jar包所在路径,并控制应用所使用的资源数量。
    编写客户端来提交任务(spark-submit) spark-submit是通过脚本启动的,这个脚本会启动main方法,这个main方法对应的类就是spark-submit要执行的程序。 Main 函数中有定义saprk执行入口: val sc = new SparkContext(), 这句话的执行效果就是让driver和master通信。 driver向master提交任务并申请资源(spark-submiy里面制定; master的位置,让driver知道matser在哪里,方便通信,并且把要申请的资源(多少核核内存)也都写在spark-submit中,一并传给master, 
  3. 然后mater看集群里面是否有符合要求的woker,和worker通信,申请资源(申请可用的worker),在之前worker向master注册的时候,matser已经掌握了worker的全部计算资源信息,此时就可以知道哪些worker的内存和核数满足该driver的请求,并且任务启动后,所有worker的资源的使用情况,master都了如指掌。收到driver的请求后,master就可决定在哪些worker上起executor. 分配的准则:让任务运行在尽量多的机器上, 
  4. master将分配的参数传给worker,worker启动executor进程 (注意起的资源的参数),woker起executor, 
  5. executor主动链接driver,driver的位置信息是通过master-worker-executor传给executor,然后实现真正的计算逻辑(比如那个RDD操作逻辑图)然后driver端生成具体的task,然后把task通过网络发给executor执行,一个executor可以执行多个task。executor 执行完毕后,将数据写入hdfs或者返回dirver
  6. driver 收到executor任务完成信号后,向cluster manager发送注销信号
  7. cluster manager向worker发送释放资源信号
  8. worker上对应的executor停止运行

四、任务层定义

按照任务层面划分:application -> job -> stage -> task

概念

定义

划分依据

Application一个spark session 对应一个application 
Job一个Job包含多个RDD及作用于相应RDD上的各种操作一个action 对应一个job
Stage是Job的基本调度单位,一个Job会分为多组任务,每组任务被称为“Stage”。

job中含有需要shuffle的操作,就会把job分解成两个stage。

job遇到宽依赖就切割stage

Task运行在Executor上的工作单元,是Executor中的一个线程。

task 个数由rdd的分区数决定。同一个stage中的所有task功能相同,只是运行在不同的分区上。

并行化的具体实现

 

 

阶段

具体动作

执行位置

构建DAG

描述了RDD的转换过程,定义了如何对数据进行操作

一个action对应一个job,对应一个DAG

DAG的开始:通过Spark Context 创建RDD

DAG的结束:一旦触发一个action,就形成一个完整的DAG

driver端
切分Stage

DAGScheduler遇到shuffle就切分stage,

stage中的所有的task的业务逻辑都相同,都装到一个set中:TaskSet中

将stage中生成的task以TaskSet的形式传给TaskScheduler

driver端
调度Task将TaskSet中的task通过网络调度到executor中driver端
执行Taskexecutor接受Task并将task反序列化,丢到线程池中执行executor端

 

 

五、RDD间依赖关系:宽窄依赖(shuffle)

宽窄依赖算子: transforamtion. 

宽依赖:需要shuffle操作的transformation算子

窄依赖:不需要shuffle操作的transformation算子

划分stage的依据就是RDD之间的宽窄依赖。遇到宽依赖就划分stage

 

为啥要切分stage?

因为业务逻辑中,需要将多台机器上具有相同属性的数据,都聚合到一台机器上(通过shuffle实现)

如果有shuffle,意味着后面阶段的数据依赖前面前一阶段的结果。

shuffle的含义:将数据打散,父RDD中一个分区的数据可能会分给子RDD中的多个分区

shuffle会有网络传输,但是网络传输不代表就是shuffle

 

依赖关系

概念

算子示例(只限transforamtaion)

窄依赖(独生子女)父RDD的一个分区去到了子RDD的一个分区,不会有shuffle产生map,filter,union
宽依赖父RDD的一个分区的数据去到了子RDD的不同分区里面。会有shuffle产生groupByKey,reduceByKey,sortByKey

六、代码实现

定义Scala启动类

Driver program包含应用的main函数,并且定义了集群上的分布式数据集RDD, 还对这些分布式数据集RDD应用了相关操作(transform 和 action)。

Driver program 通过一个 SparkContext 对象来访问Spark, SparkContext代表对计算集群的一个连接。是使用spark功能的入口点。主要用于创建和操作RDD,进行数据计算。一旦有了SparkContext,就可以用它来创建RDD,并执行操作。

// 定义scala的object, 也是咱们的启动类,会作为参数传到spark submit中
Object Test{
 
//创建 main 函数
Def main(args:Array[String]){
 
/*----------------------------创建一个SparkSession对象来访问Spark,通过对于spark-core工件的maven依赖(参考pom 文件),连接到计算集群----------------------------*/
//这个SparkSession 对象就代表着对集群的依赖,之后对于数据的任何操作,都是通过spark这个变量来操作
val spark = SparkSession.builder()
//集群URL,告诉spark如何连接到集群上,此处我们使用的是local,这个特殊值可以让Spark运行在单机单线程上而无需连接到集群,也就是所有的运算都是在本地跑的,不调动集市资源
.master("local[3]")
//当连接到一个集群时,这个值可以帮助我们在集群管理器的用户界面上找到我的应用
.appName("potential_label_set_score_2")
.getOrCreate()
 
/*----------------------------定义RDD的操作----------------------------*/
//通过spark变量(SparkSession 对象,代表着对集群的依赖)来创建RDD
val tagValue=spark.read.option("header","true").option("inferSchema","true").format("csv").load("D:\\GaoQian2\\经海路高潜重构\\测试\\tagValue.csv")
 
//对RDD执行各种操作,ex:count()
tagValue.count()
 
//对RDD执行筛选操作,筛选出含有"python"的句子,并定义内联函数
Val pythonLines = tagValue.filter(line => line.contains("Python"))

 

pom 文件

为了实现连接Spark,要给整个Scala应用添加一个对于saprk-core工件的Maven 依赖。(就是pom.xml中定义的各种东西,通过maven这个包管理工具,可以连接到公共仓库中的程序库,比如在pom.xml中加入下列的依赖,再在main脚本中初始化SparkContext对象,就可以实现对spark集群的连接,

Spark Core 实现了Spark 的基本功能,包含任务调度、内存管理、错误恢复、与存储系统交互等模块。Spark Core 提供了创建和操作这些集合的多个API (比如filter, map, reduce等)

<dependency>
  <groupId>org.apache.spark</groupId>
  <artifactId>spark-core_2.11</artifactId>
  <version>${spark.version}</version>
  <scope>provided</scope>
 </dependency>
由于Spark Core 包已经在各个工作节点的classpath 中了,所以我们把对Spark Core 的依赖标记为provided,这样当我们稍后使用assembly 方式打包应用时,就不会把spark-core包也打包到assembly包中。
如果是在本地运行,那么就可以把provide注释掉,因为本地运行不需要打包,而之前provide是为了避免把spark-core打包进去
<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-core_2.11</artifactId>
    <version>${spark.version}</version>
<!--<scope>provided</scope>-->
</dependency>
 

spark submit 脚本

#!/usr/bin/env bash
 
#submit表示提交sparkapplication, 它会起一个程序(就是java/python/scala中的main函数)红色的参数都是传给spark-submit的,告诉它master在哪里,要运行啥程序
spark-submit \  
# 要执行的类名
 --class PotentialLabelSetScore2 \
# 集群资源调度器为yarn
 --master yarn \
# 如果是client模式,那么任务执行的打印日志都会收回到driver端,比如我们在堡垒机上起任务,那么在堡垒机上就能看到日志输出,cluster模式,就要去saprk UI上看输出
 --deploy-mode client \
# 每个executor使用的12g内存
 --executor-memory 12g \
# 每个executor用多少核
 --executor-cores 3 \
# 使用的executor个数
 --conf spark.dynamicAllocation.maxExecutors=1000 \
 --conf spark.sql.shuffle.partitions=3000 \
 --conf spark.network.timeout=1000s \
 --conf spark.dynamicAllocation.enabled=true \
 --conf spark.hadoop.hive.lzo.use.index=true \
 --conf spark.sql.orc.impl=hive \
 --conf spark.sql.orc.enableVectorizedReader=true \
 --conf spark.sql.hive.convertMetastoreOrc=true \
 --conf spark.yarn.queue=root.bdp_jmart_scr_union.bdp_jmart_dwm_union.bdp_jmart_dwm_formal \
 --jars AutoFeatureEngineering-1.0-SNAPSHOT.jar\
FashionUserSegmentation-1.0-SNAPSHOT.jar  加在jar包后面的参数才是给程序的 > predict2.log #程序中打印出来的部分都写到predict2.log这个中

总结

本文对spark的基本原理进行了整理。内容较多,框架性还是不足够。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值