spark原理和概念
spark 运行架构
spark的节点分为 driver(驱动节点)和executor(执行节点),基于yarn来提交spark job分为两种模式client和cluster,两种模式去区别在于 client模式将会把driver程序运行在执行spark-submit的机器上,而cluster会把driver程序传输到集群中的一个节点去执行, client模式如果中断了spark-submit进程,job将会结束
这里要区分一下yarn的 resourceManager/NodeManager, 这两个概念和spark的driver和executor是独立的 , 在yarn的nodeManager上spark不仅可以运行 driver而且可以运行executor
spark-submit参数
主要为一下几个
--deploy-mode 指定client模式或者cluster模式
--class 指定主类 --jars job打包后的jar,也可以传递job依赖的第三方jar(也可以采用其他方式指定第三方jar比如放到每个节点的固定路径 或者hdfs上)
--executor-memory 执行节点需要的内存 --driver-memory 驱动节点需要的内存
spark任务的分解
driver会将用户写的代码转换成多个task,task是spark的最小工作单位,在转化过程中 spark会对程序逻辑进行一些优化,这些逻辑优化被称为stage,每个stage由过个task组成,spark 根据是否发生shuffle 划分stage
Spark的shuffle过程
spark将job换分为多个 stage,每个stage是一个大粒度的DAG(有向无环图),划分依据就是shuffle,spark的shuffle分为 write和read(写和读)两个部分,分属于两个stage, write是父rdd的最后一步,read是子rdd的第一步(类似于 MapReduce 从map到reduce的shuffle过程)
- Shuffle write 当stage完成最后一个rdd的计算的时候,先判断是否需要对结果进行聚合,然后将最终结果按照不同的reduce端进行区分,写入当前节点的本地磁盘,这个写入的算法 在spark1.2之前有hash shuffle 1.2之后新增了 sort shuffle(带排序),在2.0之后只保留了sort shuffle
- map端的combiner操作
- 写入内存如果内存满了 写入磁盘
- shuffle read 当reduce端任务开始读取rdd时,先拉取map端的数据(这个数据可能在本地也可能在远程节点),之后按照key进行聚合,判断是否需要排序,之后生成新的rdd
- block fetch 读取各个map节点上的数据
- reduce 端的combiner
- 进行排序
spark driver将用户代码转换成task的过程见下图,spark driver程序会根据数据所在的位置分配任务给不同的executor
spark 编程基础概念
rdd
rdd是spark的一个重要概念,全称 弹性分布式数据集,spark的任何操作都是基于这种数据结构,spark程序的基本流程都是 提取/创建rdd,转换rdd,基于不同的rdd进行计算,spark会将rdd的数据分发到集群上进行并行计算,每个rdd被分为多个分区(partition),这些分区运行在不同的节点上,
rdd的基本操作:
rdd的基本操作分为两种 transformation(转换)和action(执行),转换是由一种结构的rdd变化为两一种结构的rdd,执行操作是针对于rdd进行计算并且把计算结果返回给 driver程序或者输出到指定位置比如hdfs hive hbase等,值得注意的是 转换操作并不会出发真正的计算只有涉及到执行操作的时候spark才会真正的开始调用资源进行计算,这种惰性计算有利于spark自动优化我们的计算逻辑,
除此之外rdd还有一种特殊操作,persist(缓存,持久化)例如将中间计算的结果缓存入本地节点的内存中,cache也可以对rdd计算的结果进行缓存他们之间的区别在于 cache本质上是调用的persist ,persist可以传递参数指定缓存的级别,比如 内存 硬盘之类的,cache默认缓存到内存
rdd的transformation操作(转换算子)
针对value类型数据
输入分区与输出分区一对一类型
- map算子 通过自定义函数过滤每个数据项
- flatMap算子 和map类似但是会将元素合并为一个集合
- mapPartitions算子 会在每个分区内对元素进行map操作
- glom算子 将每个分区形成一个数组
输入分区与输出分区一对多类型
- union 参考sql union, 连接两个集合
- cartesian 对两个两个rdd内的元素进行求笛卡尔积的操作
输入分区与输出分区多对多类型
- groupBy 分组
输出分区为输入分区子集类型
- filter 过滤
- distinct 去重复
- subtract 集合求差集
- sample 相对比例采样
- takeSample 设定个数采样
缓存类型
- cache 默认缓存到内存
- persist 可以指定缓存级别
key-value类型的transformation算子
输入分区与输出分区一对一
- mapValues 对key-value的数的 value进行map操作
对单个rdd聚合
- combineByKey 根据key进行value的组合
- reduceByKey 根据key 合并value
- partitionByKey 根据key重新分区
两个rdd聚合
- cogruop 对两个rdd进行协同划分
连接
- jion 参考sql连接操作
- leftOutJoin/rightOutJoin 参考sql连接操作
Action算子(行动算子)
无输出类型
- foreach(f) 对每个集合元素应用函数f
HDFS
- saveAsTextFile算子 输出到hdfs指定目录
- aveAsObjectFile算子 将分区中的每10个元素组成一个Array,然后将这个Array序列化,映射为(Null,BytesWritable(Y))的元素,写入HDFS为SequenceFile的格式
Scala集合和数据类型
- collect算子 将全部分布式的rdd返回为一个单机的 array数组 并且在这个数组上应用函数f
- collectAsMap算子 对 key-value类型的rdd 返回一个单机的hashmap,对于重复的元素后面的覆盖前面的
- reduceByKeyLocally算子 先进行reduce 在进行 collectAsMap
- lookup算子返回指定Key对应的元素形成的Seq。 这个函数处理优化的部分在于,如果这个RDD包含分区器,则只会对应处理K所在的分区,然后返回由(K,V)形成的Seq。 如果RDD不包含分区器,则需要对全RDD元素进行暴力扫描处理,搜索指定K对应的元素。
- count算子 返回整个rdd的元素个数
- top算子 取top n
- reduce算子 reduce操作是对rdd中的两个元素 a b 进行一个计算,然后将结果继续与下一个元素进行相同的计算
- fold算子
- aggregate算子 输入值和返回值的类型可以不一致,接受一个初始值,在各个分区之间应用f1函数,然后分区之间应用f2函数最后得到一个结果进行返回