spark原理解析和spark core

spark原理解析

  • 解析一:resilient distributed dataset (RDD)

resilient distributed dataset (RDD):弹性分布式数据集,有容错机制可并行执行。

分布式即体现在每个rdd分多个partition,每个partition在执行时为一个task,会被master分配到某一个worker执行器(Executor)的某一个core中。

弹性体现在流水线思想(pipe),即rdd方法分为transformations和actions方法,两者的官方解释为:RDDs support two types of operations: transformations, which create a new dataset from an existing one, and actions, which return a value to the driver program after running a computation on the dataset。transformations类方法在执行过程中,只会记录每个rdd的依赖,不会立即执行,在这个过程中,可以弹性的处理partition。当action类方法执行时,会按照依赖,每个rdd去父rdd中要数据。

  • 解析二:窄依赖(完全依赖)和宽依赖(部分依赖)

transformations类方法的依赖分为窄依赖(完全依赖)和宽依赖(部分依赖),窄依赖可以理解为:每个父rdd的一个分区的数据只会给一个子rdd的一个分区(一个task的数据只会给流水线下游的一个task),子rdd的分区中数据来自一个多个父rdd的分区的数据;宽依赖肯定会有某些或全部父rdd的task数据给多个子rdd的task。

当宽依赖时,需要进行shuffle,此时,会按照shuffle切分成一个个stage。

整个job的过程是一个有向无环图(DAG),如下图,是rdd方法leftOuterJoin执行时的一个DAG,rdd leftOuterJoin是宽依赖,因此要划分stage,并会发生shuffle;当触发action类方法如collect时会按照依赖往dirver拉数据时,会从rdd leftOuterJoin的task中拿数据,从而自下而上,触发整个流水线作业。

Dependency
	NarrowDependency(窄依赖)
		OneToOneDependency
		RangeDependency
	ShuffleDependency(宽依赖)

 

  • 解析三:shuffle
ShuffleManager
	HashShuffleManager
	SortShuffleManager(默认)

目前默认的shuffle方式为:SortShuffleManager

由于一个worker上运行多个task,每个worker上生成的所有临时文件数是reduce的数量

具体reduceByKey的shuffle过程如下,在map端会进行shuffle写,会先写到缓存,然后写到磁盘;在reduce端会进行shuffle读,读取时会判断取远程读还是在本机读,读取时也会先写到缓存。

shuffle时,等map端的父stage写完后,reduce端才会去进行fetch,fetch的时候是边fetch边处理,不会等全部fetch完再处理。

另外一种方式,hashShuffle,每个worker上会生成map*reduce个磁盘文件,会增大磁盘io以及内存的压力。

shuffle涉及的设置如下:

1、shuffle方式(sort、hash)
spark.shuffle.manager
2、spark.shuffle.file.buffer.kb
shuffle写入缓存的大小(默认32kb)
3、spark.reducer.maxMbInFlight
shuffle读(reduce端)缓存大小(默认48m)
4、spark.shuffle.compress
shuffle写入磁盘是否压缩,默认true
5、spark.shuffle.io.maxRetries
shuffle读通过netty fetches读时,失败的最大尝试次数,默认3
6、spark.shuffle.io.retryWait
5中每次等待几秒(默认5s)
7、spark.shuffle.service.index.cache.size

 

  • 解析四:task数量

当transformation方法时,就确定了map和reduce的task数量。

一般一个worker启动一个Executor,默认每个Executor使用的core数(同一时间一个core只能运行一个task)为机器的所有核心数(即每个CPU的核数相加)

使用rdd方法创建一个rdd时,如果运行在cluster模式下,partition默认的数量为所有Executor的总core数。

reduce的partition的数量。由于reduce可能来自多个rdd,如果没有自己实现分区器(partition)时,使用的是默认的分区器,此时如果配置文件没有配置参数时,使用的是父rdd的最大分区数,源码如下:

     *reduce分区的数量
     * 先按rdd1、rdd2的分区数进行降序排列,此时按续遍历,如果发现有rdd自己定时了partitioner,就返回自己定义的;如果没有定义,去查询spark.default.parallelism,如果没有该配置,返回父rdd分区数最高的一个分区;上面rdd3的分区为3个,取最高的
     * 默认是HashPartitioner 还有一个RangePartitioner
   def defaultPartitioner(rdd: RDD[_], others: RDD[_]*): Partitioner = {
    val bySize = (Seq(rdd) ++ others).sortBy(_.partitions.size).reverse
    for (r <- bySize if r.partitioner.isDefined && r.partitioner.get.numPartitions > 0) {
      return r.partitioner.get
    }
    if (rdd.context.conf.contains("spark.default.parallelism")) {
      new HashPartitioner(rdd.context.defaultParallelism)
    } else {
      new HashPartitioner(bySize.head.partitions.size)
    }
  }

解析五:打包发布,使用资源管理yarn

程序如下:

object TestSparkCore {
  def main(args: Array[String]): Unit = {
    // 1. 创建spark context
    val conf = new SparkConf()
    conf.setAppName("first") // 设置应用程序的名字 
//         conf.setMaster("spark://focuson1:7077")  // 等价于 --master 参数
    conf.set("spark.shuffle.manager", "hash") // 修改shuffle的实现类
    val sc = new SparkContext(conf)
    
    test_reduceByKey(sc)

    sc.stop()

  }

 

打成jar包,传到focuson1上,执行下面语句。

spark-submit --master yarn-cluster --class com.bd.spark.core.TestSparkCore my_first_app.jar

--master  yarn-cluster 使用yarn或spark://ip:7077
--class 执行的类的,有包要写上包
--conf 配置 如--conf spark.shuffle.manager=hash
--driver-class-path jar包路径,不会发布到全部worker
--jars jar包路径,会发布到全部worker
--application-arguments 传递给主方法的参数

*像conf、appname等程序的优先级大于spark-submit

spark core

  1. core之rdd方法
  • rdd action
 
  1. /*

  2.  * actions方法

  3.    //collect,从每个worker中拿数据,在driver中显示。driver可能会oom

  4.   //takeordered是升序从每个分区中(一个rdd有多个分区,每个分区是一个task)拿出i个数据,拿到driver进行比较,拿出i个数据

  5.   //top是降序,类似takeordered

  6.  */

  7.  def test_reduce(sc: SparkContext) = {

  8. val rdd = sc.makeRDD(List("hello world", "hello count", "world spark"))

  9. rdd.reduce((x, y) => (x + y))

  10. }

  11. def test_countApprox(sc: SparkContext) = {

  12. //在数据量特别大,不需要精确结果时,求一个近似值

  13. val rdd3 = sc.parallelize(List(1, 2, 3, 4, 5, 6))

  14. rdd3.countApprox(1000l)

  15. }

  16. def test_saveAsTextFile(sc: SparkContext) = {

  17. val rdd = sc.parallelize(List(1, 2, 3, 4, 5, 6), 2)

  18. rdd.saveAsTextFile("hdfs://focuson1:9000/spark")

  19. }

  • rdd transformation
 
  1. def test_flatMap(sc: SparkContext) = {

  2. val rdd = sc.makeRDD(List("hello world", "hello count", "world spark"), 2)

  3. val rdd2 = rdd.flatMap { x => x.split(" ") }

  4. println(rdd2.collect())

  5. //res1: Array[String] = Array(hello, world, hello, count, world, spark)

  6. }

  7.  
  8. def test_union(sc: SparkContext) = {

  9. //执行时,在一个stage内,分为三个rdd 求并积 分区是rdd1+rdd2

  10. val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5))

  11. val rdd2 = sc.parallelize(List(5, 6, 7, 8, 9, 10))

  12. val rdd3 = rdd1 ++ rdd2

  13. //res54: Array[Int] = Array(1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10)

  14. }

  15.  
  16. def test_cartesian(sc: SparkContext) {

  17. //分区数为rdd1*rdd2 笛卡尔积

  18. val rdd1 = sc.parallelize(List("dog", "cat", "tiger"))

  19. val rdd2 = sc.parallelize(List(1, 2))

  20. rdd1.cartesian(rdd2)

  21. //res5: Array[(String, Int)] = Array((dog,1), (dog,2), (cat,1), (cat,2), (tiger,1), (tiger,2))

  22. }

  23. //mapPartitionsWithIndex

  24. def test_mapPartitionsWithIndex(sc: SparkContext) = {

  25. val rdd = sc.parallelize(List(1, 2, 3, 4, 5, 6), 2)

  26. var list = List[String]()

  27. // 分区0的所有数据+a , 分区1的数据 +b

  28. rdd.mapPartitionsWithIndex((i, iter) => {

  29. while (iter.hasNext) {

  30. if (i == 0)

  31. list = list :+ (iter.next + "a")

  32. else {

  33. list = list :+ (iter.next + "b")

  34. }

  35. }

  36. list.iterator

  37. })

  38. }

  39. def test_zip(sc: SparkContext) = {

  40. //两个rdd元素必须相等

  41. val list = sc.makeRDD(List(43, 5, 2, 5, 6, 33))

  42. val list2 = sc.makeRDD(List("a", "b", "c", "d", "e", "f"))

  43. list.zip(list2).collect

  44. //res29: Array[(Int, String)] = Array((43,a), (5,b), (2,c), (5,d), (6,e), (33,f))

  45.  
  46. }

  47. def test_reparition(sc: SparkContext) = {

  48. val rdd3 = sc.parallelize(List(1, 2, 3, 4, 5, 6), 2)

  49. rdd3.coalesce(4) //默认是false,即分区由多变少,此时由2变为4不能成功,还是两个分区

  50. rdd3.coalesce(4, true) //此时会成功

  51. /**

  52. * def coalesce(numPartitions: Int, shuffle: Boolean = false)

  53. * 默认是false,即分区由多变少,有多变少不会进行shuffle;true时会进行分区,此时会进行shuffle

  54. */

  55.  
  56. /*

  57. def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {

  58. coalesce(numPartitions, shuffle = true)

  59. }

  60. */

  61. rdd3.repartition(4) //相当于 rdd3.coalesce(4, true)

  62. }

  63. def test_reduceByKey(sc: SparkContext) = {

  64. //reduceByKey就是把key值进行分组,然后每组内进行reduce

  65. val rdd = sc.makeRDD(List(("hello", 1), ("hello", 1), ("hello", 1), ("world", 1), ("world", 1)))

  66. val rdd2 = rdd.reduceByKey { (x, y) => x + y }

  67. //res2: Array[(String, Int)] = Array((hello,3), (world,2))

  68. }

  69.  
  70. def test_intersection(sc: SparkContext) = {

  71. //取两个rdd的交集

  72. val rdd1 = sc.parallelize(List("dog", "cat", "tiger"), 2)

  73. val rdd2 = sc.parallelize(List("dog", "wolf", "pig"), 3)

  74. val rdd3 = rdd1.intersection(rdd2)

  75. //res23: Array[String] = Array(dog)

  76. }

  77.  
  78. def test_sortBy(sc: SparkContext) = {

  79. val list = sc.makeRDD(List(43, 5, 2, 5, 6, 33))

  80. list.sortBy(x => x) //升序

  81. list.sortBy(x => x, false) //降序

  82. }

  83.  
  84. def test_aggregateByKey(sc: SparkContext) = {

  85. import scala.math._

  86. val rdd = sc.parallelize(List(("pig", 3), ("cat", 2), ("dog", 5), ("cat", 4), ("dog", 3), ("cat", 3), ("cat", 7), ("cat", 4)), 2)

  87.  
  88. rdd.aggregateByKey(0)((x, y) => x + y, (x, y) => x * y)

  89. /* partition:[0]

  90. (pig,3)

  91. (cat,2)

  92. (dog,5)

  93. (cat,4)

  94. partition:[1]

  95. (dog,3)

  96. (cat,3)

  97. (cat,7)

  98. (cat,4)*/

  99.  
  100. //同一个分区内根据key进行分组,然后每组的value值进行第一个表达式的reduce操作

  101. /* partition:[0]

  102. (pig,3)

  103. (cat,6)

  104. (dog,5)

  105. partition:[1]

  106. (dog,3)

  107. (cat,14)

  108. (cat,7)

  109. */

  110. //然后对各个分区的所有数据按key进行分区,然后按对value值进行reduce

  111. //res38: Array[(String, Int)] = Array((dog,15), (pig,3), (cat,84))

  112.  
  113. //参数0(zeroValue)是指参与第一个表达式的运算,即每个分区内按分区之后每个组都有一个zeroValue值。如果rdd.aggregateByKey(100)((x,y)=>x+y, (x,y)=>x*y)

  114. rdd.aggregateByKey(100)((x, y) => x + y, (x, y) => x * y)

  115. /* partition:[0]

  116. (pig,3)

  117. (cat,2)

  118. (dog,5)

  119. (cat,4)

  120. partition:[1]

  121. (dog,3)

  122. (cat,3)

  123. (cat,7)

  124. (cat,4)*/

  125.  
  126. //同一个分区内根据key进行分组,然后每组的value值进行第一个表达式的reduce操作

  127. /* partition:[0]

  128. (pig,103)

  129. (cat,106)

  130. (dog,105)

  131. partition:[1]

  132. (dog,103)

  133. (cat,114)

  134. (cat,107)

  135. */

  136. //然后对各个分区的所有数据按key进行分区,然后按对value值进行reduce

  137. //res40: Array[(String, Int)] = Array((dog,10815), (pig,103), (cat,12084))

  138. }

  139.  
  140. def test_cogroup(sc: SparkContext) = {

  141. val rdd1 = sc.parallelize(List(("cat", 1), ("dog", 1), ("cat", 3)))

  142. val rdd2 = sc.parallelize(List(("cat", 2), ("dog", 2)))

  143. rdd1.cogroup(rdd2)

  144.  
  145. //res49: Array[(String, (Iterable[Int], Iterable[Int]))] = Array((dog,(CompactBuffer(1),CompactBuffer(2))), (cat,(CompactBuffer(1, 3),CompactBuffer(2))))

  146.  
  147. }

  148.  
  149. def test_combineByKey(sc: SparkContext) = {

  150. }

  151.  
  152. def test_groupBykey(sc: SparkContext) = {

  153. val rdd1 = sc.parallelize(List(("cat", 1), ("dog", 1), ("cat", 3), ("dog", 2)))

  154. rdd1.groupByKey()

  155. //res46: Array[(String, Iterable[Int])] = Array((dog,CompactBuffer(1, 2)), (cat,CompactBuffer(1, 3)))

  156.  
  157. //Groupbykey会将所有的数据发给reducer,reducer压力会比较大,另外会比较占用网络带宽, 相比之下,reduceByKey, 会在mapper端首先进行运算,reducer的压力小,另外也可以节省网络带宽

  158.  
  159. }

  160.  
  161. def test_join(sc: SparkContext) = {

  162. val rdd1 = sc.parallelize(List(("cat", 1), ("dog", 1), ("cat", 3)))

  163. val rdd2 = sc.parallelize(List(("cat", 2), ("dog", 2), ("tiger", 2)))

  164. rdd1.join(rdd2)

  165. //Array((dog,(1,2)), (cat,(1,2)), (cat,(3,2))) 将两个rdd集合中key相同的元素连接在一起 没有tiger

  166. }

  167.  
  168. def test_leftOuterJoin(sc: SparkContext) = {

  169. val rdd1 = sc.parallelize(List(("cat", 1), ("dog", 1), ("cat", 3), ("wolf", 1)))

  170. val rdd2 = sc.parallelize(List(("cat", 2), ("dog", 2), ("tiger", 2)))

  171. val array = rdd1.leftOuterJoin(rdd2) // 左外连接

  172.  
  173. //Array((wolf,(1,None)), (dog,(1,Some(2))), (cat,(1,Some(2))), (cat,(3,Some(2))))

  174.  
  175. for ((k, v) <- array) {

  176. println("key:" + k + " value:" + v._2.getOrElse(0))

  177. }

  178.  
  179. }

  • cache
  def test_cache(sc: SparkContext) = {
    val rdd = sc.parallelize(List()).cache() //缓存 (不会压缩)
    /*
   /** Persist this RDD with the default storage level (`MEMORY_ONLY`). */
  def persist(): this.type = persist(StorageLevel.MEMORY_ONLY)

  /** Persist this RDD with the default storage level (`MEMORY_ONLY`). */
  def cache(): this.type = persist()
     */
    val rdd2 = sc.parallelize(List()).persist() //该方法等于cache,默认是MEMORY_ONLY
    /*
     spark.storage.memoryFraction = 0.6//这个意思表示0.6的内存作为缓存,其余的作为计算内存
     
     StorageLevel.DISK_ONLY 只存到磁盘
     StorageLevel.DISK_ONLY_2 在其他worker也缓存一份
     StorageLevel.MEMORY_AND_DISK
     StorageLevel.MEMORY_AND_DISK2
     StorageLevel.MEMORY_AND_DISK_SER//SER表是序列化压缩
     StorageLevel.MEMORY_AND_DISK_SER2
     StorageLevel.MEMORY_ONLY_SER
     StorageLevel.MEMORY_ONLY_SER2
     StorageLevel.NONE
     StorageLevel.OFF_HEAP//Similar to MEMORY_ONLY_SER, but store the data in off-heap memory. This requires off-heap memory to be enabled. 
     */
    val rdd3 = sc.parallelize(List()).persist(StorageLevel.OFF_HEAP)
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值