Spark基础及架构

为什么使用Spark

MapReduce编程模型的局限性

  • 繁杂
    • 只有Map和Reduce两个操作,复杂的逻辑需要大量的样板代码
  • 处理效率低:
    • Map中间结果写磁盘,Reduce写HDFS,多个Map通过HDFS交换数据
    • 任务调度与启动开销大
  • 不适合迭代处理、交互式处理和流式处理

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初体验

spark-shell:Spark自带的交互式工具
本机:spark-shell --master local[*]
Standalone:spark-shell --master spark://MASTERHOST:7077
YARN:spark-shell --master yarn-client
在这里插入图片描述

安装Spark

***************** 配置spark **********************
tar -zxf spark-2.3.4-bin-hadoop2.6.tgz
mv spark-2.3.4-bin-hadoop2.6 /opt/soft/spark234
cd /opt/soft/spark234/conf
ls
cp spark-env.sh.template spark-env.sh
cp slaves.template slaves
vi slaves
vi spark-env.sh
export SPARK_MASTER_HOST=192.168.56.100
export SPARK_MASTER_PORT=7077
export SPARK_WORKER_CORES=2
export SPARK_WORKER_MEMORY=3g
export SPARK_MASTER_WEBUI_PORT=8888
cd ../sbin/
vi spark-config.sh
export JAVA_HOME=/opt/soft/jdk180
cd /opt/soft/spark234/sbin/
./start-all.sh
jps  worker、master、Jps
cd ../bin/
./spark-shell

Spark架构设计

运行架构
在这里插入图片描述
在驱动程序中,通过SparkContext主导应用的执行
SparkContext可以连接不同类型的Cluster Manager(Standalone、YARN、Mesos),连接后,获得集群节点上的Executor
一个Worker节点默认一个Executor,可通过SPARK_WORKER_INSTANCES调整
每个应用获取自己的Executor
每个Task处理一个RDD分区

Spark架构核心组件

在这里插入图片描述

Spark API SparkContext

SparkContext
连接Driver与Spark Cluster(Workers)
Spark的主入口
每个JVM仅能有一个活跃的SparkContext
SparkContext.getOrCreate
在这里插入图片描述
pom:
在这里插入图片描述

Spark API 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()

使用IDEA初始化Spark运行环境

需求说明
创建Maven项目,添加依赖

  • scala-library
  • spark-core
  • spark-sql

创建SparkContext
创建SparkSession
使用Spark实现WordCount

Spark API SparkSession

RDD:Spark核心,主要数据抽象,数据集
Dataset:从Spark1.6开始引入的新的抽象,特定领域对象中的强类型集合,它可以使用函数或者相关操作并行地进行转换等操作
DataFrame:DataFrame是特殊的Dataset

Spark RDD概念

简单的解释

  • RDD是将数据项拆分为多个分区的集合,存储在集群的工作节点上的内存和磁盘中,并执行正确的操作

复杂的解释

  • RDD是用于数据转换的接口
  • RDD指向了存储在HDFS、Cassandra、HBase等、或缓存(内存、内存+磁盘、仅磁盘等),或在故障或缓存收回时重新计算其他RDD分区中的数据

RDD是弹性分布式数据集
**分布式数据集

  • RDD是只读的、分区记录的集合,每个分区分布在集群的不同节点上
  • RDD并不存储真正的数据,只是对数据和操作的描述

**弹性

  • RDD默认存放在内存中,当内存不足,Spark自动将RDD写入磁盘

**容错性

  • 根据数据血统,可以自动从节点失败中回复分区

RDD与DAG

两者是Spark提供的核心抽象
DAG(有向无环图)反映了RDD之间的依赖关系
在这里插入图片描述

RDD的特性

一系列的分区(分片)信息,每个任务处理一个分区
每个分区上都有compute函数,计算该分区中的数据
RDD之间有一系列的依赖
分区器决定数据(key-value)分配至哪个分区
优先位置列表,将计算任务分派到其所在处理数据块的存储位置

RDD编程流程

RDD创建–> RDD转换–> RDD持久化–> RDD执行

RDD的创建

使用集合创建RDD

val rdd = sc.parallelize(List(1,2,3,4,5,6))
rdd.count
rdd.partitions.size
val rdd = sc.parallelize(List(1,2,3,4,5,6),5)
rdd.partitions.size
val rd = sc.makeRDD(List(1,2,3,4,5,6))

1.Spark默认会根据集群的情况来设置分区的数量,也可以通过parallelize()第二参数来指定
2.Spark会为每一个分区运行一个任务进行处理

scala> val rdd = sc.parallelize(List(1,2,3,4,5,6))
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[17] at parallelize at <console>:24

scala> rdd.count
res4: Long = 6

scala> rdd.partitions.size
res5: Int = 4   虚拟机分了4核,默认4个区

scala> val rdd = sc.parallelize(List(1,2,3,4,5,6,7,8,9,10))
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[18] at parallelize at <console>:24

scala> rdd.partitions.size
res6: Int = 4

scala> val rdd = sc.parallelize(List(1,2,3))
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[19] at parallelize at <console>:24

scala> rdd.partitions.size
res7: Int = 4

scala> val rdd = sc.parallelize(List(1,2,3,4,5,6,7,8,9,10),3)3个片
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[20] at parallelize at <console>:24

scala> rdd.partitions.size
res8: Int = 3

scala> val rdd = sc.parallelize(List(1,2,3,4,5,6,7,8,9,10),60)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[21] at parallelize at <console>:24

scala> rdd.partitions.size
res9: Int = 60

scala> val rdd = sc.parallelize(List(1,2,3,4,5,6,7,8,9,10),3)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[22] at parallelize at <console>:24

scala> rdd.partitions.foreach(println)
org.apache.spark.rdd.ParallelCollectionPartition@a17
org.apache.spark.rdd.ParallelCollectionPartition@a18
org.apache.spark.rdd.ParallelCollectionPartition@a19

scala> rdd.partitions.foreach(x => x.index)

scala> rdd.partitions.foreach(x => println(x.index))
0
1

通过加载文件产生RDD

// 文件中的一行文本作为RDD的一个元素
val distFile = sc.textFile("file:///home/hadoop/data/hello.txt")
disFile.count
val distHDFSFile = sc.textFile("hdfs://hadoop000:8020/hello.txt")

1.加载"file://…"时,以local运行仅需一份本地文件,以Spark集群方式运行,应保证每个节点均有该文件的本地副本
2.支持目录、压缩文件以及通配符

sc.textFile("/my/directory")
sc.textFile("/my/directory/*.txt")
// 1、Spark默认访问HDFS
sc.textFile("/my/directory/*.gz")
// 2、Spark默认为HDFS文件的每一个数据块创建一个分区,也可以通过textFile()第二个参数指定,但只能比数据块数量多

其他创建RDD的方法
SparkContext.wholeTextFiles():可以针对一个目录中的大量小文件返回<filename,fileContent>作为PairRDD
普通RDD:org.apache.spark.rdd.RDD[data_type]
PairRDD:org.apache.spark.rdd.RDD[(key_type,value_type)]

SparkContext.sequenceFileK,V:Hadoop SequenceFile的读写支持

SparkContext.hadoopRDD()、newAPIHadoopRDD():从Hadoop接口API创建

SparkContext.objectFile():RDD.saveAsObjectFile()的你操作

RDD创建方式的最佳实践
测试环境:

  • 使用内存集合创建RDD
  • 使用本地文件创建RDD

生产环境:

  • 使用HDFS文件创建RDD

RDD分区

分区是RDD被拆分并发送到节点的不同块之一
我们拥有的分区越多,得到的并行性就越强
每个分区都是被分发到不同Worker Node的候选者
每个分区对应一个Task
在这里插入图片描述

RDD操作

分为lazy和non-lazy两种
Transformation(lazy):也称转换操作、转换算子
Actions(non-lazy):立即执行,也称为动作操作、动作算子

RDD转换算子

对于转换操作,RDD的所有转换都不会直接计算结果
仅记录作用于RDD上的操作
当遇到动作算子(Action)时才会进行真正计算
在这里插入图片描述

RDD常用的转换算子

map算子
对RDD中的每个元素都执行一个指定的函数来产生一个新的RDD
任何原RDD中的元素在新RDD中都有且只有一个元素与之对应
输入分区与输出分区一一对应

// 将原RDD中每个元素都乘以2来产生一个新的RDD
val a = sc.parallelize(1 to 9)
val b = a.map(x => x * 2)
a.collect
b.collect
// map把普通RDD编程PairRDD
val a = sc.parallelize(List("dog","tiger","lion","cat","panther","eagle"))
val b = a.map(x => (x,1))
b.collect

filter算子
对元素进行过滤,对每个元素应用指定函数,返回值为true的元素保留在新的RDD中

val a = sc.parallelize(1 to 10)
a.filter(_%2 == 0).collect
a.filter(_<4).collect
// map & filter
val rdd = sc.parallelize(List(1 to 6))
val mapRdd = rdd.map(_*2)
mapRdd.collect
val filterRdd = mapRdd.filter(_>5)
filterRdd.collect

mapValues算子
原RDD中的Key保持不变,与新的Value一起组成新的RDD中的元素,仅适用于PairRDD

val a = sc.parallelize(List("dog","tiger","lion","cat","panther","eagle"))
val b = a.map(x => (x.length,x))
b.mapValues("x"+_+"x").collect
// 输出结果:
Array((3,xdogx),(5,xtigerx),(4,xlionx),(3,xcatx),(7,xpantherx),(5,xeaglex))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值