spark是基于内存的分布式计算框架,特点是快速、易用、通用及多种运行模式。
快速:
基于内存数据处理,比MR快100个数量级以上(逻辑回归算法测试)
基于硬盘数据处理,比MR快10个数量级以上
易用:
支持Java、Scala、Python、R语言
交互式shell方便开发测试
通用性:
一栈式解决方案:批处理、交互式查询、实时流处理、图计算及机器学习
多种运行模式:
YARN、Mesos、EC2、Kubernetes、Standalone、Local
spark架构主要由以下组件构成:
spark 中 master,worker,driver 和 executor 之间的关系
在开发过程中,常用API主要有: SparkContext、 SparkSession、 RDD、 DataSet及 DataFrame,本文主要介绍 SparkContext、 SparkSession。
在IDEA中创建 SparkContext ,代码如下:
//导包
import org.apache.spark.{SparkConf, SparkContext}
//创建一个SparkContext对象
val conf=new SparkConf().setMaster("local[2]").setAppName("HelloSpark")
val sc=SparkContext.getOrCreate(conf)
在IDEA中创建 SparkSession ,代码如下:
//导包
import org.apache.spark.sql.SparkSession
//创建一个SparkSession对象
val spark = SparkSession.builder
.master("local[2]")
.appName("appName")
.getOrCreate()
val sc: SparkContext = spark.sparkContext
RDD称为 弹性分布式数据集( Resilient Distributed Datasets),它是一种分布式的内存抽象,允许在大型集群上执行基于内存的计算( In Memory Computing),为用户屏蔽了底层复杂的计算和映射环境。
弹性: 指在任何时候都能进行重算,这样当集群中的一台机器挂掉而导致存储在其上的RDD丢失后,Spark还可以重新计算出这部分的分区的数据
分布式: 数据计算分布于多节点
数据集: RDD并不存储真正的数据,只是对数据和操作的描述。它是只读的、分区记录的集合,每个分区分布在集群的不同节点上
简单来说,RDD是将数据项拆分为多个分区的集合,存储在集群的工作节点上的内存和磁盘中,并执行正确的操作 。
更规范的解释是:
每个分区上都有compute函数,计算该分区中的数据
RDD有依赖性,通常情况下一个 RDD是来源于另一个 RDD,这个叫做 lineage。RDD会记录下这些依赖,方便容错。也称 DAG。
只有 Key-Value 类型的 RDD才有分区器 ,可以传递一个自定义的 Partitioner 进行重新分区,非 Key-Value类型的 RDD(PairRDD)分区器的值是 None。
不同的 RDD的compute函数逻辑各不一样,比如:
该列表存储了存取每个分区的优先位置 。对于一个 HDFS文件来说,这个列表保存了每个分区所在的数据块的位置。按照 “移动数据不如移动计算的” 的理念, Spark在进行任务调度的时候,会尽可能的将计算任务移动到所要处理的数据块的存储位置。
通过集合创建RDD有两种方法:parallelize与 makeRDD
makeRDD多一个重载方法:重载分配一系列本地Scala集合形成一个RDD,可以为每个集合对象创建一个分区,并指定优先位置便于在运行中优化调度。
使用本地集合创建RDD的问题在于:由于这种方法需要用到一台机器中集合的全部数据,所以这种方式在测试和原型构造之外很少使用,一般在测试时使用
使用 parallelize 创建RDD:
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object SparkContextDemo extends App {
//创建一个spark context对象
val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("sparkTest") //Spark默认会根据集群的情况来设置分区的数量,也可以通过parallelize()第二参数来指定
val sc: SparkContext = SparkContext.getOrCreate(conf)
//创建rdd1,不指定分区
val rdd1: RDD[Int] = sc.parallelize(List(1,2,3,4,5,6,7,8))
println(rdd1.partitions.size) //控制台打印:2
//创建rdd2,指定分区:5
private val rdd2: RDD[Int] = sc.parallelize(List(1,2,3,4,5,6),5)
println(rdd2.partitions.size) //控制台打印:5
//关闭资源
sc.stop()
}
import org.apache.spark.SparkContext
import org.apache.spark.sql.SparkSession
object SparkSessionDemo extends App {
//创建一个spark session对象
val spark: SparkSession = SparkSession.builder()
.master("local[2]")
.appName("sparkSessionTest")
.getOrCreate()
val sc: SparkContext = spark.sparkContext
//加载本地文件
val distFile=sc.textFile("file:///val distFile=sc.textFile("file:///home/hadoop/data/hello.txt")")
//加载hdfs文件
val distHDFSFile=sc.textFile("hdfs://hadoop000:9000/hello.txt")
//关闭资源
sc.stop()
}
注:
sc.textFile("/my/directory")
sc.textFile("/my/directory/*.txt")
sc.textFile("/my/directory/*.gz")
SparkContext.wholeTextFiles(): 可以针对一个目录中的大量小文件返回
也可以通过键值对集合创建PairRDD:sc.parallelize(List((1,2),(1,3)))
示例: IDEA src的data目录中有两个文件:hello.txt、test01.txt
内容如下:
创建一个pairPDD读取数据:
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object CreatRDDDemo extends App{
//创建一个spark context对象
val conf:SparkConf = new SparkConf().setMaster("local[2]").setAppName("sparkTest")
val sc:SparkContext = SparkContext.getOrCreate(conf)
//加载文件
val pairRDD:RDD[(String,String)] = sc.wholeTextFiles("file:///D:\\work\\date\\2020\\spark0904\\src\\data")
pairRDD.foreach(println)
sc.stop()
}