Spark简单介绍
Spark是一个基于内存的快速、通用、可扩展的大数据分析计算引擎,由Scala开发。Java,scala,python,R和SQL都可以访问SparkAPI。
背景:
Matei Zaharia开发,作为加州大学伯克利分校博士论文的一部分,spark第一个版本于2012年发布。2013年Spark代码库被捐赠给Apacha Software Foundation,并成为其旗舰项目。
功能:
允许用户读取,转换,聚合数据,可以轻松训练和部署复杂的统计模型,或者打包成为要部署在集群上的库。或通过notebook交互式执行快速的分析。
包含:SparkCore,SparkSQL,SparkStreaming,Spark MLib,Spark GraphX
spark原理相关的内容可参考spark原理介绍和scala打包介绍
用于大数据处理的集群计算框架。与Hadoop紧密集成,可以在yarn上运行,并支持Hadoop文件格式及其hdfs。
spark最突出的表现是将作业间产生的大规模工作数据集存储在内存中。
优点:擅长迭代算法和交互式分析,拥有丰富的API集。
spark运行架构
核心组件:
-
driver
负责托管sparkcontext并为作业调度任务
-
executor
专属于应用,在应用运行期间运行并执行该应用的任务
spark自身资源调度–Master 和 Worker
Master 和 Worker,Master 是一个进程,主要负责资源的调度和分配,并进行集群的监控等职责,类似于 Yarn 环境中的 RM, 而Worker 也是进程,一个 Worker 运行在集群中的一台服务器上,由 Master 分配资源对数据进行并行的处理和计算,类似于 Yarn 环境中 NM。
ApplicationMaster
Hadoop 用户向 YARN 集群提交应用程序时,提交程序中应该包含ApplicationMaster,用于向资源调度器申请执行任务的资源容器 Container,运行用户自己的程序任务 job,监控整个任务的执行,跟踪整个任务的状态,处理任务失败等异常情况。简单点就是,ResourceManager(资源)和 Driver(计算)之间的解耦合靠的就是ApplicationMaster。
提交流程
spark:
1.作业提交
2.DAG构建
3.任务调度
4.任务执行
基于yarn(Spark 应用程序提交到 Yarn 环境中执行的时候,一般会有两种部署执行的方式:Client和 Cluster。主要区别在于:Driver 程序的运行节点位置。)
Spark核心编程
Spark 计算框架为了能够进行高并发和高吞吐的数据处理,封装了三大数据结构,用于处理不同的应用场景。三大数据结构分别是:
➢ RDD : 弹性分布式数据集
➢ 累加器:分布式共享只写变量
➢ 广播变量:分布式共享只读变量
1.RDD
1.1设计背景
许多迭代式算法(比如机器学习、图算法等)和交互式数据挖掘工具,共同之处是,不同计算阶段之间会重用中间结果
目前的MapReduce框架都是把中间结果写入到稳定存储(比如磁盘)中,带来了大量的数据复制、磁盘IO和序列化开销提供了一个抽象的数据架构,不必担心底层数据的分布式特性,只需将具体的应用逻辑表达为一系列转换处理,不同RDD之间的转换操作形成依赖关系,可以实现管道化,避免中间数据存储
1.2概念
Spark 中最基本的数据处理模型。代码中是一个抽象类,它代表一个弹性的、不可变、可分区、里面的元素可并行计算的集合。
• 一个RDD就是一个分布式对象集合,本质上是一个只读的分区记录集合,每个RDD可分成多个分区,每个分区就是一个数据集片段,并且一个RDD的不同分区可以被保存到集群中不同的节点上,从而可以在集群中的不同节点上进行并行计算
• RDD提供了一种高度受限的共享内存模型,即RDD是只读的记录分区的集合,不能直接修改,只能基于稳定的物理存储中的数据集创建RDD,或者通过在其他RDD上执行确定的转换操作(如map、join和group by)而创建得到新的RDD
•RDD提供了一组丰富的操作以支持常见的数据运算,分为“动作”(Action)和“转换”(Transformation)两种类型
•RDD提供的转换接口都非常简单,都是类似map、filter、groupBy、join等粗粒度的数据转换操作,而不是针对某个数据项的细粒度修改(不适合网页爬虫)
•表面上RDD的功能很受限、不够强大,实际上RDD已经被实践证明可以高效地表达许多框架的编程模型(比如MapReduce、SQL、Pregel)
•Spark用Scala语言实现了RDD的API,程序员可以通过调用API实现对RDD的各种操作
1.3RDD运行原理
1.4RDD特性
1.5RDD依赖关系(Shuffle操作、窄依赖和宽依赖)
1.6阶段划分
Spark根据DAG图中的RDD依赖关系,把一个作业分成多个阶段。对于宽依赖和窄依赖而言,窄依赖对于作业的优化很有利。只有窄依赖可以实现流水线优化,宽依赖包含Shuffle过程,无法实现流水线方式处理。Spark通过分析各个RDD的依赖关系生成了DAG,再通过分析各个RDD中的分区之间的依赖关系来决定如何划分Stage,具体划分方法是:
•在DAG中进行反向解析,遇到宽依赖就断开
•遇到窄依赖就把当前的RDD加入到Stage中 •将窄依赖尽量划分在同一个Stage中,可以实现流水线计算
1.7RDD运行过程总结
1.8基础编程
1.8.1创建RDD
两种方式:读取外部数据集,在驱动程序中对一个集合进行并行化
- 从外部存储(文件)创建 RDD
由外部存储系统的数据集创建 RDD 包括:本地的文件系统,所有 Hadoop 支持的数据集,比如 HDFS、HBase 等。
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("spark")
val sparkContext = new SparkContext(sparkConf)
val fileRDD: RDD[String] = sparkContext.textFile("input")
fileRDD.collect().foreach(println)
sparkContext.stop()
- 从集合(内存)中创建 RDD
Spark 主要提供了两个方法:parallelize 和 makeRDD(从底层代码实现来讲,makeRDD 方法其实就是 parallelize 方法)
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("spark")
val sparkContext = new SparkContext(sparkConf)
val rdd1 = sparkContext.parallelize(
List(1,2,3,4)
)
val rdd2 = sparkContext.makeRDD(
List(1,2,3,4)
)
rdd1.collect().foreach(println)
rdd2.collect().foreach(println)
sparkContext.stop()
- 从其他 RDD 创建
- 直接创建 RDD(new)
2.累加器
累加器用来把 Executor 端变量信息聚合到 Driver 端。在 Driver 程序中定义的变量,在Executor 端的每个 Task 都会得到这个变量的一份新的副本,每个 task 更新这些副本的值后,传回 Driver 端进行 merge。
3.广播变量
广播变量用来高效分发较大的对象。向所有工作节点发送一个较大的只读值,以供一个或多个 Spark 操作使用。比如,如果你的应用需要向所有节点发送一个较大的只读查询表,广播变量用起来都很顺手。在多个并行操作中使用同一个变量,但是 Spark 会为每个任务分别发送
实践
下载spark
集群启动spark-shell --master local[2]
单机启动 spark-shell
linux scala实践相关
sbt设置
sbt是一款Spark用来对scala编写程序进行打包的工具,Spark 中没有自带 sbt,
curl https://bintray.com/sbt/rpm/rpm > bintray-sbt-rpm.repo
mv bintray-sbt-rpm.repo /etc/yum.repos.d/
yum install sbt
检验 sbt 是否可用
sbt sbt-version
#!/bin/bash
SBT_OPTS="-Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M"
java $SBT_OPTS -jar `dirname $0`/sbt-launch.jar "$@"
在./sparkapp 中新建文件 simple.sbt(vim ./sparkapp/simple.sbt),添
加内容如下,声明该独立应用程序的信息以及与 Spark 的依赖关系:
name := “Simple Project”
version := “1.0”
scalaVersion := “2.12.10”
libraryDependencies += “org.apache.spark” %% “spark-core” % “3.0.0”
sbt编译打包
编译打包
sbt打包:/usr/bin/sbt package
生成的 jar 包的位置为 /usr/local/spark/sparkapp/simple-project_2.12-1.0.jar
创建sparkapp2,文件与sparkapp内容一致,并新建pom.xml
<project>
<groupId>cn.edu.xmu</groupId>
<artifactId>simple-project</artifactId>
<modelVersion>4.0.0</modelVersion>
<name>Simple Project</name>
<packaging>jar</packaging>
<version>1.0</version>
<repositories>
<repository>
<id>jboss</id>
<name>JBoss Repository</name>
<url>http://repository.jboss.com/maven2/</url>
</repository>
</repositories>
<dependencies>
<dependency> <!-- Spark dependency -->
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<scalaVersion>2.11.8</scalaVersion>
<args>
<arg>-target:jvm-1.5</arg>
</args>
</configuration>
</plugin>
</plugins>
</build>
</project>
mvn编译打包 /usr/bin/mvn package
生成的应用程序JAR包的位置为“sparkapp2/target/simple-project-1.0.jar”。
提交应用
/usr/local/spark/spark-3.0.2-bin-without-hadoop/bin/spark-submit --class "SimpleApp" /usr/local/spark/sparkapp/target/scala-2.12/simple-project_2.12-1.0.jar
可以通过spark-submit提交应用程序,该命令的格式如下:
./bin/spark-submit
--class <main-class> //需要运行的程序的主类,应用程序的入口点
--master <master-url> //Master URL,下面会有具体解释
--deploy-mode <deploy-mode> //部署模式
... # other options //其他参数
<application-jar> //应用程序JAR包
[application-arguments] //传递给主类的主方法的参数
Spark的运行模式取决于传递给SparkContext的Master URL的值。
Master URL可以是以下任一种形式:
- local 使用一个Worker线程本地化运行SPARK(完全不并行)
- local[*] 使用逻辑CPU个数数量的线程来本地化运行Spark
- local[K] 使用K个Worker线程本地化运行Spark(理想情况下,K应该根
据运行机器的CPU核数设定) - spark://HOST:PORT 连接到指定的Spark standalone master。默认端
口是7077. - yarn-client 以客户端模式连接YARN集群。集群的位置可以在
HADOOP_CONF_DIR 环境变量中找到。 - yarn-cluster 以集群模式连接YARN集群。集群的位置可以在
HADOOP_CONF_DIR 环境变量中找到。 - mesos://HOST:PORT 连接到指定的Mesos集群。默认接口是5050。
(Spark 主要是计算框架,而不是资源调度框架,所以本身提供的资源调度并不是它的强项)
提交应用
bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode cluster \
./examples/jars/spark-examples_2.12-3.0.0.jar \
10
例子:
import org.apache.spark.SparkContent._
import org.apache.spark.{SparkConf, SparkContent}
Object MaxTemperature {
def main(args:Array[Strings]){
val conf = new SparkConf().setAppName('max temperature')
val sc = new SparkConf(conf)
sc.textFile(args(0))
.map(_.split('\t'))
.filter(rec => (rec(1) != '9999' &&rec(2).matches('[01459]')
.map(rec => (rec(0).toInt, rec(1).toInt))
.reduceByKey((a,b) => Math.max(a,b))
.saveAsTextFile('output')
}
}
执行:
spark-submit --class 类名 --master指定作业应该在哪运行
spark-submit --class MaxTemperature --master local\
spark-examples.jar input/sample.txt output
spark-submit --class MaxTemperature --master local\
***/***/MaxTemperature.py input/sample.txt output
windows scala相关
上手
1.配置环境,scala,maven
创建test.scala
object Test {
def main(args: Array[String]): Unit = {
println("Hello Spark")
}
}
实战-word count
思路:
windows:
hadoop:
安装和配置参考:https://www.cnblogs.com/wuxun1997/p/6847950.htmll
成功
spark:下载安装包:https://www.apache.org/dyn/closer.lua/spark/spark-3.1.1/spark-3.1.1-bin-hadoop2.7.tgz,解压安装包,配置环境变量
成功:
实现:
1.建立连接
2.执行业务操作
3.关闭连接
报错:可参考window failed to locate the winutils binary in the hadoop binary path 。。。
scala代码如下:
object Spark01_WordCount {
def main(args: Array[String]): Unit = {
// Application
// Spark框架
// TODO 建立和Spark框架的连接
// JDBC : Connection
//val sparConf = new SparkConf().setMaster("local").setAppName("WordCount")
val sparConf = new SparkConf().setMaster("local").setAppName("WordCount")
val sc = new SparkContext(sparConf)
// TODO 执行业务操作
// 1. 读取文件,获取一行一行的数据
// hello world
val lines: RDD[String] = sc.textFile("datas")
// 2. 将一行数据进行拆分,形成一个一个的单词(分词)
// 扁平化:将整体拆分成个体的操作
// "hello world" => hello, world, hello, world
val words: RDD[String] = lines.flatMap(_.split(" "))
// 3. 将数据根据单词进行分组,便于统计
// (hello, hello, hello), (world, world)
val wordGroup: RDD[(String, Iterable[String])] = words.groupBy(word=>word)
// 4. 对分组后的数据进行转换
// (hello, hello, hello), (world, world)
// (hello, 3), (world, 2)
val wordToCount = wordGroup.map {
case ( word, list ) => {
(word, list.size)
}
}
// 5. 将转换结果采集到控制台打印出来
val array: Array[(String, Int)] = wordToCount.collect()
array.foreach(println)
// TODO 关闭连接
sc.stop()
}
}
结果:
进阶
def main(args: Array[String]): Unit = {
// Application
// Spark框架
// TODO 建立和Spark框架的连接
// JDBC : Connection
val sparConf = new SparkConf().setMaster("local").setAppName("WordCount")
val sc = new SparkContext(sparConf)
// TODO 执行业务操作
// 1. 读取文件,获取一行一行的数据
// hello world
val lines: RDD[String] = sc.textFile("datas")
// 2. 将一行数据进行拆分,形成一个一个的单词(分词)
// 扁平化:将整体拆分成个体的操作
// "hello world" => hello, world, hello, world
val words: RDD[String] = lines.flatMap(_.split(" "))
// 3. 将单词进行结构的转换,方便统计
// word => (word, 1)
val wordToOne = words.map(word=>(word,1))
// 4. 将转换后的数据进行分组聚合
// 相同key的value进行聚合操作
// (word, 1) => (word, sum)
val wordToSum: RDD[(String, Int)] = wordToOne.reduceByKey(_+_)
// 5. 将转换结果采集到控制台打印出来
val array: Array[(String, Int)] = wordToSum.collect()
array.foreach(println)
// TODO 关闭连接
sc.stop()
}
其他一些API
dataframe
dataframe像RDD一样,是分布在集群的节点中不变的数据集合,与RDD不同的是,在DataFrame中,数据是以命名列的方式组织的。
目的在使大型数据集处理更容易,允许开发人员对数据结构进行形式化,允许更高级的抽象。主要要点使,Spark引擎一开始就构建了一个逻辑执行计划,而且执行生成的代码使基于成本优化程序确定的物理计划。与Java,Scala相比,python中的RDD非常慢,而DataFrame的引入则使性能在各种语言都保持稳定。
dataset
允许用户轻松表达域对象的转换,同事还提供了具有强大性能和优点的SparkSQL引擎。
Catalyst优化器
Spark SQL支持SQL查询和DataFrame API。Spark SQL的核心是Catalyst优化器。优化器是基于函数式编程结构,并且意在实现两个目的:简化向Spark SQL的添加新的优化技术和特性的条件,并允许外部开发人员扩展优化器
参考:
Spark编程基础课件
尚硅谷Spark教程(Spark3.0重磅发布)代码和课件
pyspark实战指南