Spark技术原理---更新中

己整理的一些问题及答案,意在简洁语言,容易理解,如有错误地方,请纠正指出。

spark工作机制:

spark工作流程

  1. client提交任务到Resource Manager RM:submit
  2. R启动一定数据量的 Node Manager NM
  3. 随机选择一个 NM 启动Spark ApplicationMaster AM启动 Driver,分发Task 到Excutor
  4. App Status直至结束
  5. Task执行完成,释放资源

RDD宽窄依赖:

  1. 宽依赖:(一对多,多对多)shuffer 过程中通过 partition 函数,将父 RDD 每个分区 key 的不同记录分发的不同的子 RDD 中 ,groupbykey ,reducerbykey
  2. 窄依赖:(一对一,多对一)父 RDD 与子 RDD 分区关系, map ,filter、union

spark MR shuffer区别

MR map的shuffle
  1. 读取的数据会放到环形缓存区,然后环形缓存区的内存达到一定的阀值的时候会把文件溢写到磁盘,溢出的各种小文件会合并成一个大文件,这个合并的过程中会进行排序,这个排序叫做归并排序。
  2. sort排序(默认按字典排序)
  3. 合并(combiner合并)
  4. 文件合并(merage 合并 总共有三种,默认是内存到磁盘)
  5. 压缩(设置压缩就会执行)
MR reduce 的shuffle

reduce端会拉取map端的数据,拉取的这个过程叫做copy过程
reduce阶段会涉及到:

  1. sort排序
  2. 分组(将相同的key的value放到一个容器的过程)
  3. merage文件合并
Spark shuffle

spark默认情况下,是不会对数据进行排序的。因此ShuffleMapTask每写入一点数据,ResultTask就可以拉取一点数据,然后在本地执行我们定义的聚合函数和算子,进行计算

区别

与MapReduce完全不一样的是,MapReduce它必须将所有的数据都写入本地磁盘文件以后,才能启动reduce操作,来拉取数据。为什么?因为mapreduce要实现默认的根据key的排序!所以要排序,肯定得写完所有数据,才能排序,然后reduce来拉取。

shuffer 调优方式

  1. Shuffle 的选择

  2. 缓冲区的大小

  3. 拉去的数据量的大小

  4. 间隔时间重试次数。

    spark.shuffle.file.buffe
    参数用于设置shuffle write task的BufferedOutputStream的buffer缓冲大小。将数据写到磁盘文件之前,会先写入buffer缓冲中,待缓冲写满之后,才会溢写到磁盘
    spark.reducer.maxSizeInFlight
    该参数用于设置shuffle read task的buffer缓冲大小,而这buffer缓冲决定了每次能够拉取多少数据
    spark.shuffle.io.maxRetries
    write task所在节点拉取属于自己的数据时,如果因为网络异常导致拉取失败,是会自动进行重试的。该参数就代表了可以重试的最大次数。

spark-submit 提交任务 Client与

  • Yarn-Client 模式同样是适用于测试,因为Driver运行在本地,Driver会与yarn集群中的Executor进行大量的通信,会造成客户机网卡流量的大量增加。方便看日志。
  • Yarn-Cluster主要用于生产环境中,因为Driver运行在Yarn集群中某一台nodeManager中,每次提交任务的Driver所在的机器都是随机的,不会产生某一台机器网卡流量激增的现象,缺点是任务提交后不能看到日志。只能通过yarn查看日志。

Shuffle数据块有多少种不同的存储方式?分别是什么

  1. RDD数据块:用来存储所缓存的RDD数据。
  2. Shuffle数据块:用来存储持久化的Shuffle数据。
  3. 广播变量数据块:用来存储所存储的广播变量数据。
  4. 任务返回结果数据块:用来存储在存储管理模块内部的任务返回结果。通常情况下任务返回结果 随任务一起通过Akka返回到Driver端。但是当任务返回结果很大时,会引起Akka帧溢出,这时的另一种方案是将返回结果以块的形式放入存储管理模块,然后在Driver端获取该数据块即可,因为存储管理模块内部数据块的传输是通过Socket连接的,因此就不会出现Akka帧溢出了。
  5. 流式数据块:只用在Spark Streaming中,用来存储所接收到的流式数据块

Cache和persist的区别

    cache调用了persist方法,cache只有一个默认的缓存级别MEMORY_ONLY ,而persist可以根据
情况设置其它的缓存级别。
    cache是将数据缓存到内存里,当小数据量的时候是能提升效率,但数据大的时候内存放不下就
会报溢出。

Scala 语法中to 和 until

to 包含上界,until不包含上界

Scala伴生对象和伴生类

    在一个源文件中,如果出现object与class名称相同的情况,那么就可以将该object成为该class的
伴生对象,该class则可以成为该object的伴生类。
    当一个源文件中,只有object而没有对应的伴生类时,object被称之为Standalone Object-独立对象
    类和伴生对象之间可以互相访问对方的私有方法和属性
    注意:必须在同一个源文件里定义类和它的伴生对象。

scala闭包

闭包 = 代码 + 用到的非局部变量

val y = 1
val sum = (x:int) => x+y
sum(2)
//结果为 3

scala中的柯里化

将原来接受两个参数的函数变成新的接受一个参数的函数的过程。
新的函数返回一个以原有的第二个参数作为参数的函数

//该函数接受两个参数
def sum(x:Int,y:Int) = x + y  
//该函数接受一个参数生成另外一个接受单个参数的函数
def sumOneAtTime(x:Int) = (y:Int) => x + y  
//这样的话,如果需要计算两个数的乘积的话只需要调用:
sumOneAtTime(5)(4)

case class和class的区别

case class:

是一个样本类,样本类是一种不可变切可分解类的语法糖,也就是说在构建的时候会自动生成一些语法糖,具有以下几个特点:

  1. 自动添加与类名一致的构造函数(也就是半生对象,通过apply方法实现),也就是说在构造对象的时候不需要使用new关键字
  2. 样本类中的参数默认是val关键字,不可以修改
  3. 默认实现了toString,equals,hashcode,copy方法
  4. 样本类可以通过==来比较两个对象,不在构造方法内地二属性不会用在比较上
class:

class是一个类
class在构造对象的时候需要使用new关键字才可以。

隐式转换

是以implicit关键字声明的带有单个参数的函数,这样的函数将被自动应用,将值从一种类型转换为另一种类型。隐式转换函数叫什么名字是无所谓的,因为通常不会由用户手动调用,而是由Scala进行调用。但是如果要使用隐式转换,则需要对隐式转换函数进行导入。

隐式值:
//name为隐式参数
def person(implicit name : String) = name   
implicit val p = "mobin"   //p被称为隐式值
person  结果为 res1: String = mobin
隐式视图:

隐式转换为目标类型:把一种类型自动转换到另一种类型

def foo(msg : String) = println(msg)
	implicit def intToString(x : Int) = x.toString
foo(10)  结果为 10
隐式转换调用类中本不存在的方法:
class SwingType{
		def  wantLearned(sw : String) = println("兔子已经学会了"+sw)
	}
	object swimming{
	  implicit def learningType(s : AminalType) = new SwingType
	}
	class AminalType
	object AminalType extends  App{
	  import com.mobin.scala.Scalaimplicit.swimming._
	  val rabbit = new AminalType
		rabbit.wantLearned("breaststroke")         //蛙泳
	}

spark checkpoint使用

  1. 在代码中,用SparkContext设置一个checkpoint目录,可以是一个容错文件系统的目录,比如HDFS
  2. .在代码中,对需要进用SparkContext设置一个checkpoint目录行checkpoint的RDD,执行RDD.checkpoint()
  3. RDDCheckpointData(Spark内部的API),会接管该RDD,标记为marked for checkpoint,准备进行checkpoint
  4. ob运行完之后,会调用一个finalRDD.doCheckpoint()方法,会顺着RDD lineage,回溯扫描,发现有标记为待checkpoint的RDD,就会进行二次标记,inProgressCheckpoint,即正在接受checkpoint操作
  5. job运行完后,会启动一个内部的新的job,去将标记为inProgressCheckpoint的RDD的数据,都写入hdfs文件中(如果rdd之前cache过,会直接从缓存中获取数据,写入hdfs中,如果没有cache过,那么就会重新计算一遍这个RDD,再checkpoint)
  6. 将checkpoint过的RDD之前的依赖RDD,改成一个CheckpointRDD*,强制改变RDD的lineage,后面如果RDD的cache数据获取失败,直接会通过它的上游CheckpointRDD,去容错的文件系统中,比如hdfs,获取Checkpoint数据Checkpoint其实是cache的一个备胎

spark executor 内存、core个数设定

  1. Job(作业):Spark根据行动操作触发提交作业,以行动操作将我们的代码切分为多个Job。
  2. Stage(调度阶段):每个Job中,又会根据宽依赖将Job划分为多个Stage(包括ShuffleMapStage和ResultStage)。
  3. Task(任务):真正执行计算的部分。Stage相当于TaskSet,每个Stage内部包含了多个Task,将各个Task下发到各个Executor执行计算。每个Task的处理逻辑完全一样,不同的是对应处理的数据。即:移动计算而不是移动数据。
  4. Partition(分区):这个是针对RDD而言的,RDD内部维护了分区列表,表示数据在集群中存放的不同位置。
  5. driver内存:collect或者广播变量比较大时,可以适当调大该值避免OOM
  6. num-executors:配置执行任务的Executor的数量。
  7. executor-cores:每个Executor的核的数量。可以理解为Executor的一个线程。
  8. 每个核同时只可以执行一个Task。也就是说一个Spark应用同时执行的任务数 = 用于执行任务的Executor数 * 每个Executor的核数。
  9. spark.executor.memory:每个Executor的内存大小。
  10. spark.default.parallelism:RDD的默认分区数。

spark task数量设定

  1. 在基于RDD计算时,Task的数量 = RDD的分区数。
  2. 输入可能以多个文件的形式存储在HDFS上,每个File都包含了很多块,称为Block。
  3. 当Spark读取这些文件作为输入时,会根据具体数据格式对应的InputFormat进行解析,一般是将若干个Block合并成一个输入分片,称为InputSplit,注意InputSplit不能跨越文件。随后将为这些输入分片生成具体的Task。InputSplit 与Task是一一对应的关系。
  4. spark.default.parallelism 用来控制shuffle过程中的task的数量 ,默认为8,需要根据实际数据量进行相应设置,不是越大越好
  5. spark.sql.shuffle.partitions:这个配置是针对于Spark SQL在shuffle时的默认分区数。默认值是200。只对Spark SQL起作用。
  6. spark.hadoop.mapreduce.input.fileinputformat.split.minsize 用来控制输入文件块的大小,当小文件太多的时候需要设置这个,但是如果设置太大,输入切分较少,也会影响并行度.
  7. rdd.repartition 代码中:来重新分区,该方法会生成一个新的rdd,使其分区数变大

scala中偏函数和柯里化的区别

    偏函数:就是固定部分参数,生成另外一个参数更少的方法
    柯里化:把一个多参数的方法,改造成可以接受单一参数的方法,并返回接受剩余参数的新函数。
    区别:柯里化是可以将n个参数的方法拆成n次调用;偏函数则是将n个参数的方法拆成n-x个参
数调用和x参数的方法。可以理解成偏函数演变成柯里化的一种不完全形式。

spark命令行传递文件为参数方式

--files参数    +文件路径,多个文件以逗号分隔

spark写文件可保存的模式:Append、Overwrite、ErrorIfExists、Ignore

通过mode控制保存的模式

  • Append:不会覆盖之前的文件,会生成新的文件,追加在后面
  • Overwrite:覆盖了以前生成的文件
  • ignore:在这个目录下如果有这种类型的文件就不会生成,如果没有就会创建
  • ErrorIfExists:如果存在就抛出异常
// 使用方式
import org.apache.spark.sql.{DataFrame, SQLContext, SaveMode}
dataFrame.write.mode(saveMode.Append)

spark udf函数

  • 注册,在sql中使用
spark.udf.register("strLen", (str: String) => str.length())
  • DataFrame API中使用
val strLen = udf((str: String) => str.length())

spark-shell

  • 窗口运行scala文件:
    spark-shell -i 路径下scala文件
    scala文件执行顺序为从上到下,调用的函数需要放在前面,注意个别依赖需要优先进行 import
  • 调参数方式:
    spark-shell --help 中参数可以直接进行添加,如果不包含需要使用 --conf spark.driver.maxResultSize=5G --conf spark.sql.shuffer.partitions=400

scala集合添加元素

  • :: 该方法被称为cons,意为构造,向队列的头部追加数据,创造新的列表。
    val list = List[Int](10,20)
	//向集合的头部添加元素,结果为 List(1, 10, 20)
	println(1 :: list)
	val list1 = List[Int](1,2)
	//将一个集合整体添加到另一个集合中,新集合的元素个数只增加一个,结果为List(List(10, 20), 1, 2),元素个数为3
	println(list :: list1)
  • :+和+: 两者的区别在于:+方法用于在尾部追加元素,+:方法用于在头部追加元素,冒号永远靠近集合类型
	val list = List[Int](10,20)
	//结果为Vector(List(10, 20), b)
    println( list +: "b")
    //结果为List(10, 20, B)
    println( list :+ "B")
  • ++ 该方法用于连接两个集合
val list1 = List[Int](10,20)
val list1 = List[Int](1,2)
//结果为 List(10, 20, 1, 2)
println(list1++list2)
  • ::: 该方法只能用于连接两个List类型的集合
val list1 = List[Int](10,20)
val list1 = List[Int](1,2)
//结果为 List(10, 20, 1, 2)
println(list1:::list2)

map 与 mapPartition 区别

  • map是对分区内的每一条数据进行处理,返回的是一个值
  • mapPartition是对一个分区的所有数据进行处理,返回一个迭代器的值,设定一个可变的list,每次取出一个值放到list中,当分区内所有值都计算完成后,list转换为迭代器进行返回。

foreach 与 foreachPartition区别

  • 使用连接池获取连接或者直接获取连接时,如果采用foreach的话,就是会给每一条数据都创建了连接,这样很浪费资源,如果采用foreachPartition的话,就是给每个分区创建一次连接,提高了资源利用率。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值