一、Spark的概述
1.1 Hadoop的回顾
- 版本号的发展
hadoop1.x : hdfs 和 mapreduce
hadoop2.x : hdfs、mapreduce、yarn、common
hadoop3.x : hdfs、mapreduce、yarn、common
- hadoop的重要模块组成
hdfs : 分布式文件存储系统 需要搭建和部署
mapreduce: 离线分析和计算框架 不需要搭建, 是程序要要开发的逻辑代码
yarn : 资源调度管理系统 需要搭建和部署
- HDFS的优点和缺点
优点:
-- datanode的可靠性、可以部署在廉价的商用硬件上(允许部分机器宕机)
-- 适合存储大数据集
-- 副本自动冗余策略(不满足3个的时候,会自动启动冗余,达到3个)
-- 集群易于横向扩展(增加节点)
缺点:
-- 不适合存储小文件
-- 只支持追加操作
-- 在同一时间内只允许一个writer
- MR的优点和缺点
优点:
-- 适合批处理(离线处理)计算大数据集
-- 易于编程
-- 容错性较好(task失败,可以进行重试)
-- 扩展性良好
缺点:
-- 不适合低延迟的工作
-- 不适合处理小文件
-- 调优困难
1.2 Spark的简介
1.2.1 Spark是什么
-1. spark是一个快速的、通用的、可扩展的大数据分析计算框架
-2. spark是用scala语言编写的
-3. spark在09年诞生、13年6月加入apache、14年2月成为顶级项目
-4. Spark快的原因:
(1)基于内存计算
(2)使用DAG执行引擎(调度)
(3)支持数据向上追踪(血缘追踪,临时存储)
-5. spark可以进行 批处理、迭代运算、交互式查询、流处理、机器学习、图处理等
-6. spark程序可以使用多种语言编写,比如java、scala、python、r语言等
-7. spark能处理的数据源,也比较多,比如可以处理mysql、hdfs、hive等各种地方的数据
-8. spark的安装部署方式有多种
本地模式: local
集群模式: standalone、yarn、mesos、k8s
1.2.2 Spark的模块组件
- Spark Core
实现了 Spark 的基本功能,包含任务调度、内存管理、错误恢复、与存储系统 交互等模块。Spark Core 中还包含了对弹性分布式数据集(resilient distributed dataset,简称RDD)的 API 定义。
- Spark SQL
是 Spark 用来操作结构化数据的程序包。通过 Spark SQL,我们可以使用 SQL 或者 Apache Hive 版本的 SQL 方言(HQL)来查询数据。Spark SQL 支持多种数据源,比 如 Hive 表、Parquet 以及 JSON 等。
- Spark Streaming
是 Spark 提供的对实时数据进行流式计算的组件。提供了用来操作数据流的 API,并且与 Spark Core 中的 RDD API 高度对应。
- Spark MLlib
提供常见的机器学习(ML)功能的程序库。包括分类、回归、聚类、协同过滤等,还提供了模型评估、数据 导入等额外的支持功能。
- Spark GraphX
GraphX在Spark基础上提供了一站式的数据解决方案,可以高效地完成图计算的完整流水作业。GraphX是用于图计算和并行图计算的新的(alpha)Spark API。通过引入弹性分布式属性图(Resilient Distributed Property Graph),一种顶点和边都带有属性的有向多重图,扩展了Spark RDD。
1.3 Spark与Hadoop的比较
1.3.1 框架比较
hadoop | spark | |
---|---|---|
起源 | 2005 | 2009 |
起源地 | MapReduce (Google) Hadoop (Yahoo) | University of California, Berkeley |
数据处理引擎 | Batch | Batch |
处理 | Slower than Spark and Flink | 100x Faster than Hadoop |
编程语言 | Java, C, C++, Ruby, Groovy, Perl, Python | Java, Scala, python and R |
编程模型 | MapReduce | Resilient distributed Datasets (RDD) |
Data Transfer | Batch | Batch |
内存管理 | Disk Based | JVM Managed |
延迟 | HIgh | Medium |
吞吐量 | Medium | High |
优化机制 | Manual | Manual |
API | Low-level | High-level |
流处理支持 | NA | Spark Streaming |
SQL支持 | Hive, Impala | SparkSQL |
Graph 支持 | NA | GraphX |
机器学习支持 | NA | SparkML |
1.3.2 处理流程比较
1)MR中的迭代:
mr的整个应用程序,通常会有多个job串联, 在整个应用程序处理过程中,一个job所需要的数据使从磁盘上读取的,job执行完,会将处理结果存储到磁盘上,因此整个作业的迭代,涉及到大量的磁盘IO,所以慢
2)Spark中的迭代:
spark整个应用程序,也会有多个job串联,在处理过程中,第一个job从磁盘上读取数据,处理结果通常情况下存储到内存里,下一个job从内存中读取数据,进行处理,依次执行下去。所以说,整个作业的迭代,通常都是基于内存的,因此处理速度更快。
1.4 Spark的入门编程
说明:
创建maven项目,引入下面的jar包依赖
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spark_sz2102</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 声明公有的属性 -->
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<encoding>UTF-8</encoding>
<scala.version>2.11.8</scala.version>
<spark.version>2.2.3</spark.version>
<hadoop.version>2.7.6</hadoop.version>
<scala.compat.version>2.11</scala.compat.version>
</properties>
<!-- 声明并引入公有的依赖 -->
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
</dependencies>
<!-- 配置构建信息 -->
<build>
<!-- 资源文件夹 -->
<sourceDirectory>src/main/scala</sourceDirectory>
<!-- 声明并引入构建的插件 -->
<plugins>
<!-- 用于编译Scala代码到class -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</args>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<!-- 程序打包 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<!-- 过滤掉以下文件,不打包 :解决包重复引用导致的打包错误-->
<filters>
<filter><artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<!-- 打成可执行的jar包 的主方法入口-->
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass></mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
1.4.1 wordcount的编写方式1
1)编程思路
1:按行读取数据,2:按照空格切分成单词 3:按照单词分组 4:统计每组的单词个数
2)代码实现
package com.qf.sparkcore.day01
import org.apache.spark.rdd.RDD
import org.apache.spark.{
SparkConf, SparkContext}
/**
* spark的入门案例: wordcount
* 思路:
* 1:按行读取数据,2:按照空格切分成单词 3:按照单词分组 4:统计每组的单词个数
*/
object WordCount1 {
def main(args: Array[String]): Unit = {
//1: 构建配置对象, 获取spark上下文对象
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("wordcount")
val sc: SparkContext = new SparkContext(conf)
//2: 读取数据 RDD[String] : 指代的就是多个String的弹性分布式数据集 每个String都是一行记录。
val lines: RDD[String] = sc.textFile("D:/data/")
//3: 按照空格切分,并展平 [spark,core,hello.......]
val words: RDD[String] = lines.flatMap((line) => line.split(" "))
//4: 按照单词分组 每个元素都是一个具有两个元素的元组 第一个元素是单词 第二个元素是这个单词的集合,
// 比如 (spark,List(spark,spark,spark)) (hello,List(hello,hello,hello))......
val wordGroup: RDD[(String, Iterable[String])] = words.groupBy(word => word)
//5:统计每一组的个数
val wordAndCount: RDD[(String, Int)] = wordGroup.mapValues(_.size)
//6: 将分布在各个计算节点的RDD搜集到客户端,并打印
val result: Array[(String, Int)] = wordAndCount.collect()
result.foreach(println)
//释放资源
sc.stop()
}
}
1.4.2 wordcount的编写方式2
1)编程思路
1:按行读取数据,2:按照空格切分成单词 3:构建元素 4:按照单词分组 5:统计每一组中的1的和
2)代码实现
package com.qf.sparkcore.day01
import org.apache.spark.rdd.RDD
import org.apache.spark.{
SparkConf, SparkContext}
/**
* spark的入门案例: wordcount
* 思路2:
* 1:按行读取数据,2:按照空格切分成单词 3:构建元素 4:按照单词分组 5:统计每一组中的1的和
*/
object WordCount2 {
def main(args: Array[String]): Unit = {
//1: 构建配置对象, 获取spark上下文对象
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("wordcount")
val sc: SparkContext = new SparkContext(conf)
//2: 读取数据 RDD[String] : 指代的就是多个String的弹性分布式数据集 每个String都是一行记录。
val lines: RDD[String] = sc.textFile("D:/data/")
//3: 按照空格切分,并展平 [spark,core,hello.......]
val words: RDD[String] = lines.flatMap((line) => line.split(" "))
//4: 将每个单词和1 构建一个kv形式的元组 (spark,1) (core,1) (hello,1) (spark,1)........
val wordAndOne: RDD[(String, Int)] = words.map((_, 1))
/* //5: 按照元组的第一个元素即单词分组: (spark,List((spark,1),(spark,1),(spark,1)) (hello,List((hello,1),(hello,1),(hello,1)))....
val wordAndList: RDD[(String, Iterable[(String, Int)])] = wordAndOne.groupBy(_._1)
//6: 统计每一组中的1的累加值
val wordAndCount: RDD[(String, Int)] = wordAndList.map(group => {
val list: Iterable[(String, Int)] = group._2
var sum = 0;
for (elem <- list) {
sum += elem._2
}
(group._1, sum)
})*/
/**
* 5 : 使用reduceByKey直接计算
* 前提:元素必须是KV形式的数据
* 作用: 通过k进行分组,并对value进行聚合 。 也就是将同一个k的分为一组,然后将同一组的所有value进行reduce
* 参数:func:(Int,Int) =>Int 是一个函数:
* 函数的第一个参数接受这一组所有value的第一个value,并进行累加
* 函数的第二个参数接受这一组所有value除了第一个value的其他value 依次累加到第一个value上
* (spark,(1,1,1)) (hello,(1,1,1))....
* (spark,3) (hello,3).....
*/
val wordAndCount: RDD[(String, Int)] = wordAndOne.reduceByKey(_ + _)
//6: 搜集,打印
val result: Array[(String, Int)] = wordAndCount.collect()
result.foreach(println)
//释放资源
sc.stop()
}
}
1.4.3 日志文件的引入
我们的目的,程序正确时,控制台只打印结果,错误时,打印错误级别的信息
在项目的resources目录下,创建文件log4j.properties,存入下面的内容
# Set everything to be logged to the console
log4j.rootCategory=ERROR, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
# Set the default spark-shell log level to WARN. When running the spark-shell, the
# log level for this class is used to overwrite the root logger's log level, so that
# the user can have different defaults for the shell and regular Spark apps.
log4j.logger.org.apache.spark.repl.Main=WARN
# Settings to quiet third party logs that are too verbose
log4j.logger.org.spark_project.jetty=WARN
log4j.logger.org.spark_project.jetty.util.component.AbstractLifeCycle=ERROR
log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=INFO
log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=INFO
log4j.logger.org.apache.parquet=ERROR
log4j.logger.parquet=ERROR
# SPARK-9183: Settings to avoid annoying messages when looking up nonexistent UDFs in SparkSQL with Hive support
log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=FATAL
log4j.logger.org.apache.hadoop.hive.ql.exec.FunctionRegistry=ERROR
二、Spark的安装部署
注意,注意,注意: spark在部署时, 不建议配置环境变量
2.1 安装部署的模式介绍
Spark的安装模式指的是运行spark程序时所需要的资源调度管理系统。
资源调度管理系统有两类,一种是本地模式,一种是集群模式。
1. 本地模式: local模式
指的是利用运行程序所在的那一台机器的系统平台,
比如在windows运行,利用的就是本地的windows系统
在linux运行,用的就是本地的windows系统
在mac运行,用的就是本地的mac系统
作用,用于开发人员测试、调试代码等,
2. 集群模式:
- Standalone:
spark本身自带的一个资源调度系统,也分为单机和集群模式
- yarn:
spark利用hadoop的yarn资源调度系统,进行spark程序的资源调度
在国内用的是最多的
- mesos:
这是一款在国外比较流行的资源调度系统
- k8s:
这个也是一种资源调度系统,出名的公司一般会用这个。
2.2 Spark的Local模式
1)上传、解压、更名
[root@qianfeng01 ~]# tar -zxvf spark-2.2.3-bin-hadoop2.7.tgz -C /usr/local/
[root@qianfeng01 ~]# cd /usr/local/
[root@qianfeng01 local]# mv spark-2.2.3-bin-hadoop2.7/ spark-local
2)启动,进入scala交互界面
[root@qianfeng01 local]# cd spark-local
[root@qianfeng01 spark-local]# ./bin/spark-shell
小贴士: 只要运行spark-shell脚本,进入scala交互界面,即成功
3)程序测试:
1. 在spark-local下的data目录,创建一个words.txt文件
hello spark
hello world
hello java
hello spark
2. 在spark-local目录下启动spark-shell指令
[root@qianfeng01 spark-local]# ./bin/spark-shell
3. 进入scala交互界面后,有两个变量可以直接使用,一个是sc(spark上下文),一个是spark(SparkSession)
scala> sc.textFile("data/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect().foreach(println)
(java,1)
(hello,4)
(world,1)
(spark,2)
2.3 Standalone集群模式
2.3.1 简要说明
standalone指的是spark程序自带的一个资源调度管理系统。支持集群模式,也支持单机模式。不管是什么模式,他都是一个主从架构,即一个活跃的master和N个worker.
master守护进程是一个管理节点,worker守护进程,有多个,是工作节点
部署安排
qianfeng01: master worker
qianfeng02: worker
qianfeng03: worker
准备条件:
三台机器的免密登录认证、防火墙关闭、时间同步、jdk1.8+,hadoop2.7.6
2.3.2 安装部署
1)上传、解压、更名
[root@qianfeng01 ~]# tar -zxvf spark-2.2.3-bin-hadoop2.7.tgz -C /usr/local/
[root@qianfeng01 ~]# cd /usr/local/
[root@qianfeng01 local]# mv spark-2.2.3-bin-hadoop2.7/ spark-standalone
PS: 如果想要使用多种模式的话,不建议配置环境变量,
2)配置spark的环境脚本
[root@qianfeng01 ~]# cd /usr/local/spark-standalone/conf/
[root@qianfeng01 conf]# cp spark-env.sh.template spark-env.sh
[root@qianfeng01 conf]# vim spark-env.sh
.......在文件末尾添加如下内容..........
export JAVA_HOME=/usr/local/jdk
export SPARK_MASTER_HOST=qianfeng01
export SPARK_MASTER_PORT=7077
解析:
环境变量SPARK_MASTER_HOST或SPARK_MASTER_IP,这两个任选其一,如果使host那就配置主机名,如果使ip就配置相关IP
7077 是master的端口号,用于内部组件之间的通信
3)配置slaves文件,即工作节点的主机名
[root@qianfeng01 conf]# cp slaves.template slaves
[root@qianfeng01 conf]# vim slaves
qianfeng01
qianfeng02
qianfeng03
- 分发到其他节点
[root@qianfeng01 ~]# scp -r /usr/local/spark-standalone/ qianfeng02:/usr/local/
[root@qianfeng01 ~]# scp -r /usr/local/spark-standalone/ qianfeng03:/usr/local/
5) 启动测试
[root@qianfeng01 spark-standalone]# ./sbin/start-all.sh
注意:
1. 查看进程是否已经启动
jps
2. 如果没有成功,需要查看日志,在spark自己的logs目录下
6)查看webUI
在浏览器上输入:
http://qianfeng01:8080
7)端口号的总结:
7077: worker和master通信的端口号
8080: master的webui端口
4040: spark-shell交互平台的一个历史作业的webui
8088: yarn的webui端口
50070: namenode的webui端口
8020: datanode和namenode的端口
16010: hmaster的webui端口
2.3.3 spark-shell脚本的应用
该脚本为spark提供了一个交互式平台,可以在此进行spark编程,以及调节spark程序。
运行模式有两种:一种是本地模式,一种是集群模式
1)本地模式的运行:
直接在命令行上输入
[root@qianfeng01 spark-standalone]# ./bin/spark-shell
2)集群模式的运行
[root@qianfeng01 ~]# /usr/local/spark-standalone/bin/spark-shell \
--master spark://qianfeng01:7077 \
--executor-memory 512m \
--total-executor-cores 1
注意:集群模式,必须指定master,其他属性可选
3)退出交互界面
正规方式:
:quit
:q
尽量不要用ctrl + c,有可能会造成4040依然被占用,也就使没有完全退出,
如果不小心ctrl+c,没有完全退出,使用命令查看监听端口 netstat - apn | grep 4040 在使用kill -9 端口号 杀死即可
2.3.4 配置Job History Server
1)说明
因为在spark程序运行期间,driver会提供webui界面,来监控并显示运行状态。但是如果程序运行完,Driver会停止该webui界面的通信,就看不到之前的运行信息了。
如果想要查看原来的运行信息,可以配置作业历史服务器,将作业运行状态信息保存到HDFS上
2)配置如下
1. 启动HDFS、在HDFS上创建一个目录,用于存储spark作业的历史记录
[root@qianfeng01 ~]# start-dfs.sh
[root@qianfeng01 ~]# hdfs dfs -mkdir /sparkJobHistory
2. 修改spark的spark-default.conf文件
[root@qianfeng01 ~]# cd /usr/local/spark-standalone/conf
[root@qianfeng01 conf]# cp spark-defaults.conf.template spark-defaults.conf
[root@qianfeng01 conf]# vim spark-defaults.conf
......
#开启历史服务
spark.eventLog.enabled true
#存储路径
spark.eventLog.dir hdfs://qianfeng01:8020/sparkJobHistory
#是否压缩
spark.eventLog.compress true
3. 修改spark-env.sh文件,添加如下配置
export SPARK_HISTORY_OPTS="-Dspark.history.ui.port=4000 -Dspark.history.retainedApplications=10 -Dspark.history.fs.logDirectory=hdfs://qianfeng01:8020/sparkJobHistory"
4. 分发两个配置文件到其他节点
[root@qianfeng01 conf]# scp -r ./spark-env.sh ./spark-defaults.conf root@qianfeng02:$PWD
[root@qianfeng01 conf]# scp -r ./spark-env.sh ./spark-defaults.conf root@qianfeng03:$PWD
5. 启动历史服务器
先启动spark,
/usr/local/spark-standalone/sbin/start-all.sh
然后启动历史服务器
/usr/local/spark-standalone/sbin/start-history-server.sh
6. 查看进程
[root@qianfeng01 conf