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
posted @ 2024-01-06 21:04  PilgrimHui  阅读(17)  评论(0编辑  收藏  举报