Linux环境Spark安装配置及使用

本文详细介绍了如何在Linux环境下安装配置Apache Spark 2.1.0,包括准备工作、安装流程、配置文件修改、环境变量设置、集群配置等。文章还深入探讨了Spark的特点、体系架构、高可用实现、任务执行、RDD概念和操作,以及Spark SQL、Spark Streaming的基础和进阶应用。最后,文章提供了基于Spark的WordCount案例,展示了RDD的算子使用和性能优化的注意事项。
摘要由CSDN通过智能技术生成

Linux环境Spark安装配置及使用

1. 认识Spark

(1) Spark介绍

  • 大数据计算引擎
  • 官网:spark.apache.org/
  • 官方介绍:Apache Spark™ is a unified analytics engine for large-scale data processing.(Apache Spark™是一个用于大规模数据处理的统一分析引擎。)
  • Spark是一种快速、通用、可扩展的大数据分析引擎,2009年诞生于加州大学伯克利分校AMPLab,2010年开源,2013年6月成为Apache孵化项目,2014年2月成为Apache顶级项目。目前,Spark生态系统已经发展成为一个包含多个子项目的集合,其中包含SparkSQL、Spark Streaming、GraphX、MLlib等子项目,Spark是基于内存计算的大数据并行计算框架。Spark基于内存计算,提高了在大数据环境下数据处理的实时性,同时保证了高容错性和高可伸缩性,允许用户将Spark部署在大量廉价硬件之上,形成集群。
  • Spark生态圈:
    • Spark Core:RDD(弹性分布式数据集)
    • Spark SQL
    • Spark Streaming
    • Spark MLLib:协同过滤,ALS,逻辑回归等等 --> 机器学习
    • Spark Graphx:图计算

(2) 为什么要学习Spark

  • Hadoop的MapReduce计算模型存在的问题:
    • MapReduce的核心是Shuffle(洗牌)。在整个Shuffle的过程中,至少会产生6次的I/O。

    • 中间结果输出:基于MapReduce的计算引擎通常会将中间结果输出到磁盘上,进行存储和容错。另外,当一些查询(如:Hive)翻译到MapReduce任务时,往往会产生多个Stage(阶段),而这些串联的Stage又依赖于底层文件系统(如HDFS)来存储每一个Stage的输出结果,而I/O的效率往往较低,从而影响了MapReduce的运行速度。

  • Spark的最大特点:基于内存
  • Spark是MapReduce的替代方案,而且兼容HDFS、Hive,可融入Hadoop的生态系统,弥补MapReduce的不足。

(3) Spark的特点:快、易用、通用、兼容

  • ——与Hadoop的MapReduce相比,Spark基于内存的运算速度要快100倍以上,即使,Spark基于硬盘的运算也要快10倍。Spark实现了高效的DAG执行引擎,从而可以通过内存来高效处理数据流。
  • 易用——Spark支持Java、Python和Scala的API,还支持超过80种高级算法,使用户可以快速构建不同的应用。而且Spark支持交互式的Python和Scala的shell,可以非常方便地在这些shell中使用Spark集群来验证解决问题的方法。
  • 通用——Spark提供了统一的解决方案。Spark可以用于批处理、交互式查询(Spark SQL)、实时流处理(Spark Streaming)、机器学习(Spark MLlib)和图计算(GraphX)。这些不同类型的处理都可以在同一个应用中无缝使用。Spark统一的解决方案非常具有吸引力,毕竟任何公司都想用统一的平台去处理遇到的问题,减少开发和维护的人力成本和部署平台的物力成本。另外Spark还可以很好的融入Hadoop的体系结构中可以直接操作HDFS,并提供Hive on Spark、Pig on Spark的框架集成Hadoop。
  • 兼容——Spark可以非常方便地与其他的开源产品进行融合。比如,Spark可以使用Hadoop的YARN和ApacheMesos作为它的资源管理和调度器,并且可以处理所有Hadoop支持的数据,包括HDFS、HBase和Cassandra等。这对于已经部署Hadoop集群的用户特别重要,因为不需要做任何数据迁移就可以使用Spark的强大处理能力。Spark也可以不依赖于第三方的资源管理和调度器,它实现了Standalone作为其内置的资源管理和调度框架,这样进一步降低了Spark的使用门槛,使得所有人都可以非常容易地部署和使用Spark。此外,Spark还提供了在EC2上部署Standalone的Spark集群的工具。

2. Spark体系架构

  • Spark的运行方式
    • Yarn
    • Standalone:本机调试(demo)
  • Worker(从节点):每个服务器上,资源和任务的管理者,只负责管理一个节点。
  • 执行过程:
    • 一个Worker 有多个 Executor。 Executor是任务的执行者,按阶段(stage)划分任务。—> RDD
  • 客户端:Driver Program 提交任务到集群中。
    • spark-submit
    • spark-shell

3. Spark-2.1.0安装流程

(1) 准备工作

  • 具备java环境
  • 配置主机名
  • 配置免密码登录
  • 防火墙关闭

(2) 解压spark-2.1.0-bin-hadoop2.7.tgz安装包到目标目录下:

  • tar -zxvf .tar.gz -C 目标目录

(3) 为后续方便,重命名Spark文件夹:

  • mv spark-2.1.0-bin-hadoop2.7/ spark-2.1.0

(4) Spark目录介绍

  • bin —— Spark操作命令
  • conf —— 配置文件
  • data —— Spark测试文件
  • examples —— Spark示例程序
  • jars
  • LICENSE
  • licenses
  • NOTICE
  • python
  • R
  • README.md
  • RELEASE
  • sbin —— Spark集群命令
  • yarn —— Spark-yarn配置

(5) 修改配置文件:

  • <1>. 配置spark-env.sh:
    • 进入spark-2.1.0/conf路径,重命名配置文件:
      • mv spark-env.sh.template spark-env.sh
    • 修改spark-env.sh信息:
      • vi spark-env.sh
      • export JAVA_HOME=/opt/module/jdk1.8.0_144
        export SPARK_MASTER_HOST=bigdata01
        export SPARK_MASTER_PORT=7077
        复制代码
  • <2>. 配置slaves:
    • 进入spark-2.1.0/conf路径,重命名配置文件:
      • mv slaves.template slaves
    • 修改slaves信息:
      • vi slaves
      • bigdata02
        bigdata03
        复制代码

(6) 配置环境变量:

  • 修改配置文件:
    • vi /etc/profile
  • 增加以下内容:
    • export SPARK_HOME=spark安装路径
    • export PATH=$PATH:$SPARK_HOME/bin
    • export PATH=$PATH:$SPARK_HOME/sbin
  • 声明环境变量:
    • source /etc/profile

(6) 集群配置:

  • 拷贝配置好的spark到其他机器上
    • scp -r spark-2.1.0/ bigdata02:$PWD
    • scp -r spark-2.1.0/ bigdata03:$PWD

(7) 启动:

  • 启动主节点:
    • start-master.sh
  • 启动从节点:
    • start-slaves.sh
  • 启动shell:
    • spark-shell
  • 通过网页端查看:

(8) 关闭:

  • 关闭主节点:
    • stop-master.sh
  • 关闭从节点:
    • stop-slaves.sh

4. Spark HA的实现

(1) 基于文件系统的单点恢复

  • 主要用于开发或测试环境。

  • 当spark提供目录保存spark Application和worker的注册信息,并将他们的恢复状态写入该目录中,一旦Master发生故障,就可以通过重新启动Master进程(sbin/start-master.sh),恢复已运行的spark Application和worker的注册信息。

  • 基于文件系统的单点恢复,主要是在spark-env.sh里对SPARK_DAEMON_JAVA_OPTS设置

    • 创建存放文件夹:mkdir /opt/module/spark-2.1.0/recovery
    • 修改配置信息:
      • vi spark-env.sh
      • 增加内容:export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=FILESYSTEM -Dspark.deploy.recoveryDirectory=/opt/module/spark-2.1.0/recovery"

(2) 基于Zookeeper的Standby Masters

  • 适用于现实生产。

  • ZooKeeper提供了一个Leader Election机制,利用这个机制可以保证虽然集群存在多个Master,但是只有一个是Active的,其他的都是Standby。当Active的Master出现故障时,另外的一个Standby Master会被选举出来。由于集群的信息,包括Worker,Driver和Application的信息都已经持久化到ZooKeeper,因此在切换的过程中只会影响新Job的提交,对于正在进行的Job没有任何的影响。加入ZooKeeper的集群整体架构如下图所示:

  • 修改配置信息:

    • vi spark-env.sh
    • 增加内容:export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER -Dspark.deploy.zookeeper.url=bigdata01:2181,bigdata02:2181,bigdata03:2181 -Dspark.deploy.zookeeper.dir=/spark"
    • 注释掉:export SPARK_MASTER_HOSTexport SPARK_MASTER_PORT
  • 发送新的配置文件到集群其余节点:

    • scp spark-env.sh bigdata02:$PWD
    • scp spark-env.sh bigdata03:$PWD

5. 执行Spark的任务

(1) spark-submit

  • 用于提交Spark的任务(任务即相关jar包)
  • e.g.: 蒙特卡洛求PI(圆周率)
    • 原理:如下图所示,随机向正方形内落点,通过统计正方形内所有点数和落入圆内的点数来计算占比,得出正方形与圆的面积近似比值,进而近似出PI值。
    • 命令:
      • spark-submit --master spark://XXXX:7077 (指明master地址) --class org.apache.spark.examples.SparkPi (指明主程序的名字) /XXXX/spark/examples/jars/spark-examples_2.11-2.1.0.jar(指明jar包地址) 100(指明运行次数)

(2) spark-shell

  • 相当于REPL,作为一个独立的Application运行
  • spark-shell是Spark自带的交互式Shell程序,方便用户进行交互式编程,用户可以在该命令行下用scala编写spark程序。
  • 参数说明:
    • --master spark://XXXX:7077 指定Master的地址
    • --executor-memory 2g 指定每个worker可用内存为2G
    • --total-executor-cores 2 指定整个集群使用的cup核数为2个
  • Spark Session 是 2.0 以后提供的,利用 SparkSession 可以访问spark所有组件
  • 两种运行模式:
    • <1>. 本地模式
      • 启动:spark-shell(后面不接任何参数)
    • <2>. 集群模式
      • 启动:spark-shell --master spark://XXXX:7077(指明master地址)
  • e.g.: 编写WordCount程序
    • <1>. 处理本地文件,把结果打印到屏幕上
      • 启动:spark-shell
      • 传入文件:sc.textFile("/XXXX/WordCount.txt")(本地文件路径).flatMap(_.split(" "))(按照空格分割).map((_,1))(单词遍历).reduceByKey(_+_)(单词计数).collect
    • <2>. 处理HDFS文件,结果保存在hdfs上
      • 启动:spark-shell --master spark://XXXX:7077(指
      • sc.textFile("hdfs://XXXX:9000/sp_wc.txt").flatMap(.split(" ")).map((,1)).reduceByKey(+).saveAsTextFile("hdfs://XXXX:9000/output/spark/WordCount")

(3) 单步运行WordCount -> RDD

  • 启动shell:spark-shell
  •   scala> val rdd1 = sc.textFile("/root/sp_wc.txt")
      rdd1: org.apache.spark.rdd.RDD[String] = /root/sp_wc.txt MapPartitionsRDD[1] at textFile at <console>:24
      
      scala> rdd1.collect
      res0: Array[String] = Array(I love Scala, I love Skark, 2019/5/8)
      
      scala> val rdd2 = rdd1.flatMap(_.split(" "))
      rdd2: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[2] at flatMap at <console>:26
      
      scala> rdd2.collect
      res1: Array[String] = Array(I, love, Scala, I, love, Skark, 2019/5/8)
      
      scala> val rdd3 = rdd2.map((_,1))
      rdd3: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[3] at map at <console>:28
      
      scala> rdd3.collect
      res2: Array[(String, Int)] = Array((I,1), (love,1), (Scala,1), (I,1), (love,1), (Skark,1), (2019/5/8,1))
      
      scala> val rdd4 = rdd3.reduceByKey(_+_)
      rdd4: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[4] at reduceByKey at <console>:30
      
      scala> rdd4.collect
      res3: Array[(String, Int)] = Array((2019/5/8,1), (love,2), (I,2), (Skark,1), (Scala,1))
    复制代码

(4) 在IDE中运行WorkCount

  • <1>. scala版本
    • import org.apache.spark.SparkConf
      import org.apache.spark.SparkContext
      
      object WordCount {
        
        def main(args: Array[String]): Unit = {
          
          //创建一个Spark配置文件
          val conf = new SparkConf().setAppName("Scala WordCount").setMaster("local")
          
          //创建Spark对象
          val sc = new SparkContext(conf)
          
          val result = sc.textFile(args(0))
            .flatMap(_.split(" "))
            .map((_, 1))
            .reduceByKey(_ + _)
            .saveAsTextFile(args(1))
      
          sc.stop()
        }
      }
      复制代码
  • <2>. Java版本
    • import java.util.Arrays;
      import java.util.Iterator;
      import java.util.List;
      
      import org.apache.spark.SparkConf;
      import org.apache.spark.api.java.JavaPairRDD;
      import org.apache.spark.api.java.JavaRDD;
      import org.apache.spark.api.java.JavaSparkContext;
      import org.apache.spark.api.java.function.FlatMapFunction;
      import org.apache.spark.api.java.function.Function2;
      import org.apache.spark.api.java.function.PairFunction;
      
      import parquet.format.PageHeader;
      import scala.Tuple2;
      
      public class WordCount {
      
      	public static void main(String[] args) {
      		// TODO Auto-generated method stub
      
      		SparkConf conf = new SparkConf()
      				.setAppName("JavaWordCount")
      				.setMaster("local") ;
      
      		//新建SparkContext对象
      		JavaSparkContext sc = new JavaSparkContext(conf) ;
      		
      		//读入数据
      		JavaRDD<String> lines = sc.textFile("hdfs://XXXX:9000/WordCount.txt") ;
      		
      		//分词 第一个参数表示读进来的话 第二个参数表示 返回值
      		JavaRDD<String> words = lines.flatMap(new FlatMapFunction<String, String>() {
      		
      		@Override
      		public Iterator<String> call(String input) throws Exception {
      			
      			return Arrays.asList(input.split(" ")).iterator() ;
      		    }
      		}) ;
      		
      		//每个单词记一次数 
      		/*
      		* String, String, Integer
      		* input   <key      value>
      		*/
      		JavaPairRDD<String, Integer> ones = words.mapToPair(new PairFunction<String, String, Integer>() {
      		
      		@Override
      		public Tuple2<String, Integer> call(String input) throws Exception {
      			
      			return new Tuple2<String, Integer>(input, 1) ;
      		}
      		}) ;
      		
      		//执行reduce操作
      		/*
      		* Integer, Integer, Integer
      		* nteger arg0, Integer arg1 返回值
      		*/
      		JavaPairRDD<String,Integer> counts = ones.reduceByKey(new Function2<Integer, Integer, Integer>() {
      		
      			@Override
      			public Integer call(Integer arg0, Integer arg1) throws Exception {
      				// TODO Auto-generated method stub
      				return arg0 + arg1 ;
      			}
      		}) ;
      		
      		//打印结果
      		List<Tuple2<String, Integer>> output = counts.collect() ;
      		
      		for (Tuple2<String, Integer> tuple :output) {
      			System.out.println(tuple._1 + " : " + tuple._2) ;
      		}
      		
      		sc.stop() ;
      		
      		}
      }
      复制代码

(5) WordCount程序处理过程

(6) Spark提交任务的流程

6. Spark的算子

(1) RDD基础

  • <1>. 什么是RDD
    • RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。RDD具有数据流模型的特点:自动容错、位置感知性调度和可伸缩性。RDD允许用户在执行多个查询时显式地将工作集缓存在内存中,后续的查询能够重用工作集,这极大地提升了查询速度。
  • <2>. RDD的属性(源码中的一段话)
    • **一组分片(Partition)。**即数据集的基本组成单位。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目。
    • **一个计算每个分区的函数。**Spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。
    • **RDD之间的依赖关系。**RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。
    • **一个Partitioner,即RDD的分片函数。**当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。
    • **一个列表。**存储存取每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。
  • <3>. RDD的创建方式
    • 通过外部的数据文件创建,如HDFS:
      • val rdd1 = sc.textFile(“hdfs://XXXX:9000/data.txt”)
    • 通过sc.parallelize进行创建:
      • val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8))
    • DD的类型:Transformation和Action
  • <4>. RDD的基本原理

(2) Transformation

  • RDD中的所有转换都是延迟加载的,也就是说,它们并不会直接计算结果。相反的,它们只是记住这些应用到基础数据集(例如一个文件)上的转换动作。只有当发生一个要求返回结果给Driver的动作时,这些转换才会真正运行。这种设计让Spark更加有效率地运行。

(3) Action

(4) RDD的缓存机制

  • RDD通过persist方法或cache方法可以将前面的计算结果缓存,但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该RDD将会被缓存在计算节点的内存中,并供后面重用。
  • 通过查看源码发现cache最终也是调用了persist方法,默认的存储级别都是仅在内存存储一份,Spark的存储级别还有好多种,存储级别在object StorageLevel中定义的。
  • 缓存有可能丢失,或者存储存储于内存的数据由于内存不足而被删除,RDD的缓存容错机制保证了即使缓存丢失也能保证计算的正确执行。通过基于RDD的一系列转换,丢失的数据会被重算,由于RDD的各个Partition是相对独立的,因此只需要计算丢失的部分即可,并不需要重算全部Partition。
    • Demo示例:
    • 通过UI进行监控:

(5) RDD的Checkpoint(检查点)机制:容错机制

  • 检查点(本质是通过将RDD写入Disk做检查点)是为了通过lineage(血统)做容错的辅助,lineage过长会造成容错成本过高,这样就不如在中间阶段做检查点容错,如果之后有节点出现问题而丢失分区,从做检查点的RDD开始重做Lineage,就会减少开销。
  • 设置checkpoint的目录,可以是本地的文件夹、也可以是HDFS。一般是在具有容错能力,高可靠的文件系统上(比如HDFS, S3等)设置一个检查点路径,用于保存检查点数据。
  • 分别举例说明:
    • <1>. 本地目录
    • 注意:这种模式,需要将spark-shell运行在本地模式上
    • <2>. HDFS的目录
    • 注意:这种模式,需要将spark-shell运行在集群模式上

(6) RDD的依赖关系和Spark任务中的Stage

  • RDD的依赖关系

    • RDD和它依赖的父RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency)。

    • 窄依赖指的是每一个父RDD的Partition最多被子RDD的一个Partition使用

      • 总结:窄依赖我们形象的比喻为独生子女
    • 宽依赖指的是多个子RDD的Partition会依赖同一个父RDD的Partition

      • 总结:窄依赖我们形象的比喻为超生
  • Spark任务中的Stage

    • DAG(Directed Acyclic Graph)叫做有向无环图,原始的RDD通过一系列的转换就就形成了DAG,根据RDD之间的依赖关系的不同将DAG划分成不同的Stage,对于窄依赖,partition的转换处理在Stage中完成计算。对于宽依赖,由于有Shuffle的存在,只能在parent RDD处理完成后,才能开始接下来的计算,因此宽依赖是划分Stage的依据

(7) RDD基础练习

  • 练习1:

  •   //通过并行化生成rdd
      val rdd1 = sc.parallelize(List(5, 6, 4, 7, 3, 8, 2, 9
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值