spark写二次排序举例、行动操作、RDD缓存、RDD依赖、spark提交过程

二次排序的案例

对下面两个文件(文件行内容有多个空格分分隔)进行二次排序:
文件:account

hadoop@apache        200
hive@apache        	   550
yarn@apache         580
hive@apache            159
hadoop@apache     300
hive@apache        258
hadoop@apache        150
yarn@apache     560
yarn@apache           260

文件:account1

zhang@apache        2
zhang@apache           5
zhang@apache     58
hadoop@apache      300
yarn@apache           100

在IDEA中写,代码如下:

def main(args: Array[String]): Unit = {
   val  conf = new SparkConf().setAppName("二次排序").setMaster("local[2]")
    val sc = new SparkContext(conf)
    val rdd = sc.textFile("file:///E:\TIMDownload\\*")   //构建 RDD,加载文件内容
		.map(x=>x.replaceAll("\\s+"," ").split(" "))	 //转换数据,将多个空格替换成一个空格,再按空格切分数据
		.map(x=>(x(0),x(1)))						//数组转换成 key,value
		.repartition(1).groupByKey()		//两个文件(分片),两个分区,要放到1个分区里面计算,所以要重分区,再按键值分组
		.mapValues(x=>x.toList.sortBy(x=>x)) //对值进行排序(升序)
		.sortByKey(true);						//对键进行排序(升序)
    println(rdd.toDebugString)			//打印输出rdd依赖关系图
    rdd.saveAsTextFile("file:///E:\\out")  //结果保存到文本
}

rdd依赖关系如下:(从下向上)


(1) ShuffledRDD[10] at sortByKey at SecondSort.scala:12 []
 +-(1) MapPartitionsRDD[9] at mapValues at SecondSort.scala:11 []
    |  ShuffledRDD[8] at groupByKey at SecondSort.scala:10 []
    +-(1) MapPartitionsRDD[7] at repartition at SecondSort.scala:10 []
       |  CoalescedRDD[6] at repartition at SecondSort.scala:10 []
       |  ShuffledRDD[5] at repartition at SecondSort.scala:10 []
       +-(3) MapPartitionsRDD[4] at repartition at SecondSort.scala:10 []
          |  MapPartitionsRDD[3] at map at SecondSort.scala:9 []
          |  MapPartitionsRDD[2] at map at SecondSort.scala:8 []
          |  file:///E:\TIMDownload\* MapPartitionsRDD[1] at textFile at SecondSort.scala:7 []
          |  file:///E:\TIMDownload\* HadoopRDD[0] at textFile at SecondSort.scala:7 []

再结合4040端口 webUI查看整个过程对的执行情况

执行结果:

(hadoop@apache,List(150, 200, 300, 300))
(hive@apache,List(159, 258, 550))
(yarn@apache,List(100, 260, 560, 580))
(zhang@apache,List(2, 5, 58))

行动操作定义

行动操作是第二种类型的 RDD 操作,它们会把最终求得的结果返回到驱动器程序,或者写入外部存储系统中触发Job,调用runJob()方法 比如:collect、count

由于行动操作需要生成实际的输出,它们会强制执行那些求值必须用到的 RDD 的转化操作。

在大多数情况下,RDD 不能通过 collect() 收集到驱动器进程中,因为它们一般都很大。
此时,我们通常要把数据写到诸如 HDFS 或 Amazon S3 这样的分布式的存储系统中。
你可以使用 saveAsTextFile()、saveAsSequenceFile(),或者任意的其他行动操作来把 RDD 的 数据内容以各种自带的格式保存起来。

foreach
说明:将结果返回值执行器节点,而非驱动器

行动操作举例

aggregate聚合函数
def aggregate[U: ClassTag](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U
说明:aggregate用于聚合RDD中的元素,先使用seqOp将RDD中每个分区中的T类型元素聚合成U类型,
     再使用combOp将之前每个分区聚合后的U类型聚合成U类型
特别注意:seqOp和combOp都会使用zeroValue的值,zeroValue的类型为U
//案例 1
val z = sc.parallelize(List(1,2,3,4,5,6), 2)
z.aggregate(0)(math.max(_,_), _ + _)
res40: Int = 9
    说明:
    step1:首先在第一个分区[0,1,2,3]中逐个元素执行math.max,结果为:3
    step2:在第二个分区[0,4,5,6]中逐个元素执行math.max,结果为:6
    stepn:在第N个分区中执行math.max,结果为:max
    step:将所有分区结果执行combOp(_+_),0+3+6=9
    
z.aggregate(5)(math.max(_, _), _ + _)
res29: Int = 16
	说明:
    step1:初始值为 5
    step2:第一分区(5, 1, 2, 3)中最大值math.max为 5
    step3:第二分区(5, 4, 5, 6) 中最大值math.max为 6
    step4:所有分区执行结果为 5 + 5 + 6 = 16

//案例 2
scala> val z = sc.parallelize(List("12","23","345","4567"),2)
z: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[3] at parallelize at <console>:24

scala> z.glom.collect
res11: Array[Array[String]] = Array(Array(12, 23), Array(345, 4567))

scala> z.aggregate("")((x,y) => math.max(x.length, y.length).toString, (x,y) => x + y)
res12: String = 42

scala> z.aggregate("")((x,y) => math.max(x.length, y.length).toString, (x,y) => x + y)
res13: String = 24
       说明:
     	step1:第一个分区中执行math.max(x.length, y.length).toString代码
                res0=:math.max(“”.length,12.length).toString =2”
				res1=:math.max(res0.length,23.length).toString =2”
	    		第一个分区最终返回值为:2
     	step2:第二个分区中执行math.max(x.length, y.length).toString代码
                res2=:math.max(“”.length,345.length).toString =3”
				res3=:math.max(res2.length,4567.length).toString =4”
	    		第一个分区最终返回值为:4
     	step3:最后执行(x,y) => x + y   :2442 
     	
scala> z.aggregate("")((x,y) => math.min(x.length, y.length).toString, (x,y) => x + y)
res14: String = 11
	   说明:
         step1:第一个分区中执行math.min(x.length, y.length).toString代码
                res0=:math.min(“”.length,12.length).toString =0”
				res1=:math.min(res0.length,23.length).toString =1”
	    		第一个分区最终返回值为:1
     	step2:第二个分区中执行math.min(x.length, y.length).toString代码
                res2=:math.min(“”.length,345.length).toString =0”
				res3=:math.min(res2.length,4567.length).toString =1”
	    		第一个分区最终返回值为:1
     	step3:最后执行(x,y) => x + y   :1111
     	
scala> z.aggregate("12")((x,y) => math.min(x.length, y.length).toString, (x,y) => x + y)
res15: String = 1211
		说明:
            step1:第一个分区中执行math.min(x.length, y.length).toString代码
            	res0=:math.min(12.length,12.length).toString =2”
				res1=:math.min(res0.length,23.length).toString =1”
	    		第一个分区最终返回值为:1
     		step2:第二个分区中执行math.min(x.length, y.length).toString代码
                res2=:math.min(12.length,345.length).toString =2”
				res3=:math.min(res2.length,4567.length).toString =1”
	    		第一个分区最终返回值为:1
     		step3:最后执行(x,y) => x + y   :12111211
fold

def fold(zeroValue: T)(op: (T, T) => T): T
说明:fold理解为aggregate的简化

val a = sc.parallelize(List(1,2,3), 3)
a.fold(0)(_ + _)
res59: Int = 6
reduceByKeyLocally

def reduceByKeyLocally(func: (V, V) => V): Map[K, V]
该函数将RDD[K,V]中每个K对应的V值根据映射函数来运算,运算结果映射到一个Map[K,V]中,而不是RDD[K,V]

scala> val a = sc.parallelize(List("dog", "cat", "owl", "gnu", "ant"), 2)
a: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[0] at parallelize at <console>:24

scala> val b = a.map(x => (x.length, x))
b: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[1] at map at <console>:26
       
scala> b.reduceByKeyLocally(_+_)
res0: scala.collection.Map[Int,String] = Map(3 -> dogcatowlgnuant) 

RDD缓存(持久化)

为了能够多次使用同一个RDD,同时避免多次计算同一个RDD,可以让Spark对数据进行持久化
让 Spark 持久化 存储一个 RDD 时,计算出 RDD 的节点会分别保存它们所求出的分区数据

  1. 默认情况下,调用Persist()函数,实现RDD数据以反序列化形式持久化至内存中。

  2. 存储级别为【org.apache.spark.storage.StorageLevel】类,存储级别默认为 MEMRORY_ONLY

    StorageLevel存储级别参数项为:磁盘、内存、堆外空间、反序列化和副本数
    private var _useDisk: Boolean,
    private var _useMemory: Boolean,
    private var _useOffHeap: Boolean,
    private var _deserialized: Boolean,
    private var _replication: Int = 1)

    缓存级别StorageLevel如下:
    val NONE = new StorageLevel(false, false, false, false)
    val DISK_ONLY = new StorageLevel(true, false, false, false)
    val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2)
    val MEMORY_ONLY = new StorageLevel(false, true, false, true)
    val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2)
    val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false)
    val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2)
    val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)
    val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2)
    val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false)
    val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2)
    val OFF_HEAP = new StorageLevel(true, true, true, false, 1)

    缓存案例: 
    //在测试本地数据时,如果文件比较大,按根据块大小32MB来切分
    def main(args: Array[String]): Unit = {
    	val  conf = new SparkConf().setAppName("持久化").setMaster("local[2]")
    	val sc = new SparkContext(conf)
    	val rdd = sc.textFile("file:///E:\\2.安装环境\\测试数据\\SogouQ\\SogouQ3.txt");
    	rdd.persist();
    	rdd.saveAsTextFile("file:///D:\\out5")
    	Thread.sleep(10000000000l)
    }
    
  3. 如果要缓存的数据太多,内存中放不下,Spark 会自动利用最近最少使用(LRU)的缓存策略把最老的分区从内存中移除
    对于仅把数据存放在内存中的缓存级别,下一次要用到 已经被移除的分区时,这些分区就需要重新计算。
    但是对于使用内存与磁盘的缓存级别的分区来说,被移除的分区都会写入磁盘。不论哪一种情况,都不必担心你的作业因为缓存了太多数据而被打断
    unpersist(),调用该方法可以手动把持久化的 RDD 从缓 存中移除

RDD依赖

  1. RDD依赖两种:宽依赖窄依赖
  2. 定义:
    在这里插入图片描述
  3. RDD依赖和RDD分区
    RDD分区–>决定Task(任务)数
    RDD依赖–>决定Stage(阶段)数
  4. 案例分析Wordcount
    a. 首先通过RDD的依赖关系划分Stage(阶段),划分阶段依据:宽依赖,即(shuffledRDD),从后往前找
    一旦找到宽依赖,就要来一刀,划分出阶段
    b. 每个Stage阶段划分Task(任务)数,任务数取决于RDD的分区数,执行从前往后
    Job总Task(任务)数:各Stage阶段任务数的总和
    在这里插入图片描述
    c.通过todebugString()方法获取血统依赖关系,划分阶段由上至下,执行任务由下至上!
案例:二次排序依赖图如下:
(1) ShuffledRDD[10] at sortByKey at SencordarySort.scala:12 []                       //ShuffledRDD第一次划分Stage
		 +-(1) MapPartitionsRDD[9] at mapValues at SencordarySort.scala:11 []
		    |  ShuffledRDD[8] at groupByKey at SencordarySort.scala:10 []                 //ShuffledRDD第二次划分Stage
		    +-(1) MapPartitionsRDD[7] at repartition at SencordarySort.scala:10 []
		       |  CoalescedRDD[6] at repartition at SencordarySort.scala:10 []
		       |  ShuffledRDD[5] at repartition at SencordarySort.scala:10 []             //ShuffledRDD第三次划分Stage
		       +-(3) MapPartitionsRDD[4] at repartition at SencordarySort.scala:10 []
			  |  MapPartitionsRDD[3] at map at SencordarySort.scala:9 []
			  |  MapPartitionsRDD[2] at map at SencordarySort.scala:8 []
			  |  file:///E:\2.安装环境\测试数据\二次排序\* MapPartitionsRDD[1] at textFile at SencordarySort.scala:7 []
			  |  file:///E:\2.安装环境\测试数据\二次排序\* HadoopRDD[0] at textFile at SencordarySort.scala:7 []

Spark提交过程

  1. 构建sc对象,[RDD创建]–>[RDD转换]–>[RDD转换]–>…–>RDD行动==>Job
    {…DAG…}
    说明:构建DAG,将DAG发送给DAGScheduler
  2. DAGScheduler(DAG调度器)作用:
    通过血统(RDD的依赖关系:宽和窄)划分Stage,并将Stage组合成TaskSet(任务数)发送给相对应集群服务器的TaskScheduler
  3. TaskSchedule(任务调度器)作用:
    将TaskSet转化为Task,并根据Task调度相应Worker的Executor
  4. Exextor执行器:运行Task
    在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值