spark小记
Spark介绍
1、Spark 是一种由 Scala 语言开发的基于内存的快速/通用/可扩展的大数据分析计算引擎。
2、Spark Core中提供了Spark最基础与最核心的功能。Spark SQL 是 Spark 用来操作结构化数据的组件。
3、MLlib 是 Spark 提供的一个机器学习算法库。GraphX 是 Spark 面向图计算提供的框架与算法库。
Spark核心概念
Spark执行时,Driver负责管理整个集群中的作业任务调度,Executor负责实际执行任务。
Driver
Spark驱动器节点,用于执行Spark任务中的main方法。Driver在Spark作业执行时主要负责
1、将用户程序转化为作业job
2、在Executor之间调度任务task
3、跟踪Executor的执行情况
4、通过UI展示查询运行情况
Executor
Executor是集群中工作节点(Worker)中的一个JVM进程,负责在Spark任务中运行具体任务(Task),任务彼此之间相互独立。Executor负责运行组成Spark应用的任务,并将结果返回给Driver进程。
RDD(弹性分布式数据集)
弹性:分区数量可以动态增减
分布式:数据存储在大数据集群不同节点上
不可变:想要改变,只能产生新的RDD,在新的RDD里封装计算逻辑
可分区、并行计算
transformation算子和action算子
RDD算子分为转换算子和行动算子
转换算子是延迟计算的,从一个RDD转换成另一个RDD。
遇到行动算子后才会触发Spark提交作业Job进行计算。
多数的行动算子都是将结果向Driver汇聚,而foreach和saveAsTextFile这两个是直接在RDD分区执行。
宽依赖和窄依赖
宽依赖表示同一个上游RDD的分区可以被多个下游RDD的分区依赖,会引起shuffle。形象比喻为多生。
窄依赖表示每一个上游RDD的分区最多被下游RDD的一个分区使用。形象比喻为独生子女。
RDD的任务划分
RDD任务划分中间分为Application=>Job=>Stage=>Task,每一层都是1对n的关系
1、Application:初始化一个SarkContext即生成一个Application
2、Job:一个Action算子就会生成一个Job,一个Job对应一个DAG执行图
3、Stage:Stage等于宽依赖的个数加1
4、Task:一个Stage阶段中,最后一个RDD的分区个数就是Task的个数
Yarn
Hadoop生态中的分布式资源调度平台,核心概念包括ResourceManager,NodeManager、ApplicationMaster、Container等。
1、RM负责处理客户端请求,资源分配与调度,监控NM,启动和监控AM
2、NM负责管理单个节点上的资源,处理来自RM和AM的命令
3、AM为应用程序申请资源并分配给内部任务
4、Container是yarn的资源抽象,封装了多维度资源(内存/cpu/磁盘等)
提交流程 (基于Yarn Cluster)
1、任务提交后向RM(ResourceManager)申请启动AM(ApplicationMaster)
2、RM分配容器,在合适的NM(NodeManager)上启动AM,此时的AM就是Driver
3、Driver启动(构建DAG Scheduler规划逻辑任务)后向RM申请Executor内存,RM分配容器,在合适的NM上启动Executor进程
4、Executor进程启动后会向Driver反向注册,Executor全部注册完成后Driver开始执行main函数
5、Driver的Task Scheduler分配逻辑任务到Executor中运行并监控(执行到Action算子时触发一个job,并根据宽依赖划分stage,每个stage生成对应的TaskSet,将task分发到各个Executor上执行)
6、Task是Spark运行中的最小单位。一个Task是一个并行,是一个pipeline(多个RDD,每个RDD只处理一个分区),由一个线程执行。
并行度
能够并行计算的任务数量,称之为并行度.
注意,并行执行的任务数量,并不是指的切分任务的数量。
RDD编程
Spark基础环境
import org.apache.spark.SparkConf
import org.apache.spark.sql.SparkSession
val conf = new SparkConf().setAppName("AppName")
val spark = SparkSession.builder().config(conf)
.config("hive.exec.dynamici.partition", true) // 可以做一些hive配置
.enableHiveSupport().getOrCreate()
val sparkContext = spark.sparkContext
RDD创建
// 从集合(内存)中创建
val rdd1 = sparkContext.parallelize(List(1,2,3,4))
// 从外部存储创建
val rdd2 = sparkContext.textFile(filePath)
// 从sql从读取,sql返回DataFrame(DataSet[Row]),通过.rdd得到RDD
val rdd3 = spark.sql("").rdd
RDD转换算子(返回rdd)
map
case class MyUid(uid:String,age:Int)
rdd4 = rdd3.map{
case Row(uid:String,age:Int) => (uid, age)
} // sql中转过来的RDD[Row] 转成Rdd[Tuple]
.map(t=> {
...
MyUid(t._1, t._2) // 包含case class的RDD可以自动转成DataFrame
})
spark.createDataFrame(rdd4) // DataFrame
mapPartitions
以分区为单位发送到计算节点,可以进行任意的处理,哪怕是过滤。
rdd.mapPartitions(datas=> {
datas.filter(_==2)
}
)
其它
flatMap:扁平化
filter:筛选
groupBy:分组
sample:采样
distinct:去重
coalesce:缩减分区,默认不shuffle
repartition:coalesce接口中shuffle为true的实现,可以缩减也可以增大分区
sortBy:排序
intersecion:交集
union:并集
substract:去除两个RDD重复部分
等等
RDD行动算子(返回值不是rdd)
reduce:聚合RDD的所有元素
collect:在Driver中以数组形式返回所有元素
count:计算RDD中元素个数
first:返回第一个
take:返回前n个
takeOrdered:返回排序后的前n个
aggregate:通过初始值和分区内聚合,再和初始值和分区间聚合
fold:aggregate的简化版操作
saveAsTextFile
saveAsObjectFile
saveAsSequenceFile
foreach:分布式遍历RDD每一个元素,调用指定函数
闭包和RDD序列化
算子以外的代码都是在Driver端执行,算子里的代码都是在Executor端执行。
如果算子内用到算子外的数据,就会形成闭包效果,如果数据无法序列化,就无法传值给Executor端执行,就会发生错误。
所以需要检查闭包内的对象是否可以进行序列化。
广播变量
广播变量用来高效分发较大的对象,向所有工作节点发送一个较大的只读值,以供一个或多个Spark操作使用。
val rdd1 = sc.makeRDD(List(("a",1)))
val broadcast = sc.broadcast(list)
rdd1.map{
case (key, num) => {
for ((k,v) <- broadcast.value) {
}
(key, num)
}
}
累加器
累加器用来把Executor端变量聚合到Driver端。Driver定义的变量,在Executor的每个Task会得到这个变量的一份新副本,每个task更新这些副本的值后,传回Driver进行merge
val rdd = sc.makeRDD(List(1,2,3))
val sum = sc.longAccmulator("sum")
rdd.foreach(
num => (
sum.add(num)
)
)
DataFrame
DataFrame和RDD的区别
1、RDD不限类型和结构,DataFrame存储结构化数据
2、RDD的泛型是任意的,DataFrame的泛型只能是Row对象
3、DataFrame可以被自动优化(存储结构单一,二维表),RDD不能被自动优化(存储太宽泛无法被针对)
4、RDD不支持spark sql,可以和spark mllib使用。DataFrame支持spark sql,一般不和spark mllib使用。
5、DataFrame其实是DataSet的特例,DataFrame也可以称为Dataset[Row]。
组成
1、结构层面,StructType对象描述整个DataFrame表结构(多个StructField),StructField描述一个列的信息(列名,列类型,是否允许为空)
2、数据层面,Row对象记录一行数据,Column对象记录一列数据并包含列信息
构建DataFrame
# 基于RDD创建,只传列名称,类型从RDD中进行推断,是否允许为空默认为允许
df = spark.createDataFrame(rdd, schema = ['name', 'age'])
# 使用StructType定义表结构来转换
schema = StructType().add("id", IntegerType(), nullable=False)
.add("name", StringType(), nullable=True)
df = spark.createDataFrame(rdd, schema)
# 基于pandas DataFrame转换
pdf = pd.DataFrame({"id":[1,2,3]})
df = spark.createDataFrame(pdf)
# 实际开发中一般通过case class转换,见以上map算子示例代码
# DataFrame转RDD
df.rdd # 此时得到RDD存储类型为Row
DataFrame基础方法
df.show():默认展示20条
df.printSchema():打印schema信息
其它:select/filter/where/groupBy/count等
df.createTempView("table") # 将df注册为临时表,可以在spark.sql中访问
df.createOrReplaceTempView("table")
df.createGlobalTempView("table") # 可在多个sparkSession使用,带前缀访问
spark.sql("select * from table limit 10") # 执行任意sql。可访问hive