Spark的调度
作业调度简介
设计者将资源进行不同粒度的抽象建模,然后将资源统一放入调度器,通过一定的算法进行调度,最终要达到高吞吐或者低访问延时的目的。
Spark在各种运行模式中各个角色实现的功能基本一致,只不过是在特定的资源管理器下使用略微不同的名称和调度机制。
Application调度
一个Application中包含多个Job,每个Job包含多个Stage,每个Stage包含多个Task,那么Application之间如何调度?多个Job之间如何调度?多个Stage之间如何调度?Task之间延时调度机制?
Standalone:FIFO模式
Job调度
就是在Application内部的一组Job集合,在Application分配到的资源量下,通过一定的算法,对每个按一定次序分配Application中资源的过程。
Job的调度模式:
FIFO模式
默认情况下,Spark的调度以FIFO的方式调度Job的执行。给个Job被切分为多个Stage。Job1优先分配需要的资源,之后提交的Job2再分配资源,依次类推。如果第一个Job没有占用满所有的资源,则第二个Job还可以继续获取剩余资源,这样多个Job可以并行运行。
FAIR模式
在FAIR共享模式调试下,Spark在多个Job之间以轮询round robin方式给任务进行资源分配,所有的任务拥有大致相当的优先级来共享集群的资源。这就意味着当一个长任务正在执行时,短任务仍可以被分配到资源,提交并执行,并且获得不错的响应时间。这样就不用像以前一样需要等待长任务执行完才可以。这种调度模式适合多用户的场景。
Tasks延时调度
数据本地性:在分布式系统下,尽量避免数据在网络上传输。一个任务的执行,需要既有任务的jar包,同时还要有数据块。所有传输任务jar包即可。
Tasks延时调度机制:
![](http://static.blog.csdn.net/images/category_icon.jpg)
原文链接:http://www.cnblogs.com/sharpxiajun/p/5506822.html
本文主要是讲解Spark里RDD的基础操作。RDD是spark特有的数据模型,谈到RDD就会提到什么弹性分布式数据集,什么有向无环图,本文暂时不去展开这些高深概念,在阅读本文时候,大家可以就把RDD当作一个数组,这样的理解对我们学习RDD的API是非常有帮助的。本文所有示例代码都是使用Scala语言编写的。
Spark里的计算都是操作RDD进行,那么学习RDD的第一个问题就是如何构建RDD,构建RDD从数据来源角度分为两类:第一类是从内存里直接读取数据,第二类就是从文件系统里读取,当然这里的文件系统种类很多常见的就是HDFS以及本地文件系统了。
第一类方式从内存里构造RDD,使用的方法:makeRDD和parallelize方法,如下代码所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/* 使用makeRDD创建RDD */ /* List */ val rdd 01 = sc.makeRDD(List( 1 , 2 , 3 , 4 , 5 , 6 )) val r 01 = rdd 01 .map { x = > x * x } println(r 01 .collect().mkString( "," )) /* Array */ val rdd 02 = sc.makeRDD(Array( 1 , 2 , 3 , 4 , 5 , 6 )) val r 02 = rdd 02 .filter { x = > x < 5 } println(r 02 .collect().mkString( "," )) val rdd 03 = sc.parallelize(List( 1 , 2 , 3 , 4 , 5 , 6 ), 1 ) val r 03 = rdd 03 .map { x = > x + 1 } println(r 03 .collect().mkString( "," )) /* Array */ val rdd 04 = sc.parallelize(List( 1 , 2 , 3 , 4 , 5 , 6 ), 1 ) val r 04 = rdd 04 .filter { x = > x > 3 } println(r 04 .collect().mkString( "," )) |
大家看到了RDD本质就是一个数组,因此构造数据时候使用的是List(链表)和Array(数组)类型。
第二类方式是通过文件系统构造RDD,代码如下所示:
1
2
3
|
val rdd : RDD[String] = sc.textFile( "file:///D:/sparkdata.txt" , 1 ) val r : RDD[String] = rdd.flatMap { x = > x.split( "," ) } println(r.collect().mkString( "," )) |
这里例子使用的是本地文件系统,所以文件路径协议前缀是file://。
构造了RDD对象了,接下来就是如何操作RDD对象了,RDD的操作分为转化操作(transformation)和行动操作(action),RDD之所以将操作分成这两类这是和RDD惰性运算有关,当RDD执行转化操作时候,实际计算并没有被执行,只有当RDD执行行动操作时候才会促发计算任务提交,执行相应的计算操作。区别转化操作和行动操作也非常简单,转化操作就是从一个RDD产生一个新的RDD操作,而行动操作就是进行实际的计算。
下面是RDD的基础操作API介绍:
操作类型 |
函数名 |
作用 |
转化操作 |
map() |
参数是函数,函数应用于RDD每一个元素,返回值是新的RDD |
flatMap() |
参数是函数,函数应用于RDD每一个元素,将元素数据进行拆分,变成迭代器,返回值是新的RDD |
|
filter() |
参数是函数,函数会过滤掉不符合条件的元素,返回值是新的RDD |
|
distinct() |
没有参数,将RDD里的元素进行去重操作 |
|
union() |
参数是RDD,生成包含两个RDD所有元素的新RDD |
|
intersection() |
参数是RDD,求出两个RDD的共同元素 |
|
subtract() |
参数是RDD,将原RDD里和参数RDD里相同的元素去掉 |
|
cartesian() |
参数是RDD,求两个RDD的笛卡儿积 |
|
行动操作 |
collect() |
返回RDD所有元素 |
count() |
RDD里元素个数 |
|
countByValue() |
各元素在RDD中出现次数 |
|
reduce() |
并行整合所有RDD数据,例如求和操作 |
|
fold(0)(func) |
和reduce功能一样,不过fold带有初始值 |
|
aggregate(0)(seqOp,combop) |
和reduce功能一样,但是返回的RDD数据类型和原RDD不一样 |
|
foreach(func) |
对RDD每个元素都是使用特定函数 |
下面是以上API操作的示例代码,如下:
转化操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
val rddInt : RDD[Int] = sc.makeRDD(List( 1 , 2 , 3 , 4 , 5 , 6 , 2 , 5 , 1 )) val rddStr : RDD[String] = sc.parallelize(Array( "a" , "b" , "c" , "d" , "b" , "a" ), 1 ) val rddFile : RDD[String] = sc.textFile(path, 1 ) val rdd 01 : RDD[Int] = sc.makeRDD(List( 1 , 3 , 5 , 3 )) val rdd 02 : RDD[Int] = sc.makeRDD(List( 2 , 4 , 5 , 1 )) /* map操作 */ println( "======map操作======" ) println(rddInt.map(x = > x + 1 ).collect().mkString( "," )) println( "======map操作======" ) /* filter操作 */ println( "======filter操作======" ) println(rddInt.filter(x = > x > 4 ).collect().mkString( "," )) println( "======filter操作======" ) /* flatMap操作 */ println( "======flatMap操作======" ) println(rddFile.flatMap { x = > x.split( "," ) }.first()) println( "======flatMap操作======" ) /* distinct去重操作 */ println( "======distinct去重======" ) println(rddInt.distinct().collect().mkString( "," )) println(rddStr.distinct().collect().mkString( "," )) println( "======distinct去重======" ) /* union操作 */ println( "======union操作======" ) println(rdd 01 .union(rdd 02 ).collect().mkString( "," )) println( "======union操作======" ) /* intersection操作 */ println( "======intersection操作======" ) println(rdd 01 .intersection(rdd 02 ).collect().mkString( "," )) println( "======intersection操作======" ) /* subtract操作 */ println( "======subtract操作======" ) println(rdd 01 .subtract(rdd 02 ).collect().mkString( "," )) println( "======subtract操作======" ) /* cartesian操作 */ println( "======cartesian操作======" ) println(rdd 01 .cartesian(rdd 02 ).collect().mkString( "," )) println( "======cartesian操作======" ) |
行动操作代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
val rddInt : RDD[Int] = sc.makeRDD(List( 1 , 2 , 3 , 4 , 5 , 6 , 2 , 5 , 1 )) val rddStr : RDD[String] = sc.parallelize(Array( "a" , "b" , "c" , "d" , "b" , "a" ), 1 ) /* count操作 */ println( "======count操作======" ) println(rddInt.count()) println( "======count操作======" ) /* countByValue操作 */ println( "======countByValue操作======" ) println(rddInt.countByValue()) println( "======countByValue操作======" ) /* reduce操作 */ println( "======countByValue操作======" ) println(rddInt.reduce((x ,y) = > x + y)) println( "======countByValue操作======" ) /* fold操作 */ println( "======fold操作======" ) println(rddInt.fold( 0 )((x ,y) = > x + y)) println( "======fold操作======" ) /* aggregate操作 */ println( "======aggregate操作======" ) val res : (Int,Int) = rddInt.aggregate(( 0 , 0 ))((x,y) = > (x. _ 1 + x. _ 2 ,y),(x,y) = > (x. _ 1 + x. _ 2 ,y. _ 1 + y. _ 2 )) println(res. _ 1 + "," + res. _ 2 ) println( "======aggregate操作======" ) /* foeach操作 */ println( "======foeach操作======" ) println(rddStr.foreach { x = > println(x) }) println( "======foeach操作======" ) |
RDD操作暂时先学习到这里,剩下的内容在下一篇里再谈了,下面我要说说如何开发spark,安装spark的内容我后面会使用专门的文章进行讲解,这里我们假设已经安装好了spark,那么我们就可以在已经装好的spark服务器上使用spark-shell进行与spark交互的shell,这里我们直接可以敲打代码编写spark程序。但是spark-shell毕竟使用太麻烦,而且spark-shell一次只能使用一个用户,当另外一个用户要使用spark-shell就会把前一个用户踢掉,而且shell也没有IDE那种代码补全,代码校验的功能,使用起来很是痛苦。
不过spark的确是一个神奇的框架,这里的神奇就是指spark本地开发调试非常简单,本地开发调试不需要任何已经装好的spark系统,我们只需要建立一个项目,这个项目可以是Java的也可以是scala,然后我们将spark-assembly-1.6.1-hadoop2.6.0.jar这样的jar放入项目的环境里,这个时候我们就可以在本地开发调试spark程序了。
大家请看我们装有scala插件的eclipse里的完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
package cn.com.sparktest import org.apache.spark.SparkConf import org.apache.spark.SparkConf import org.apache.spark.SparkContext import org.apache.spark.rdd.RDD object SparkTest { val conf : SparkConf = new SparkConf().setAppName( "xtq" ).setMaster( "local[2]" ) val sc : SparkContext = new SparkContext(conf) /** * 创建数据的方式--从内存里构造数据(基础) */ def createDataMethod() : Unit = { /* 使用makeRDD创建RDD */ /* List */ val rdd 01 = sc.makeRDD(List( 1 , 2 , 3 , 4 , 5 , 6 )) val r 01 = rdd 01 .map { x = > x * x } println( "===================createDataMethod:makeRDD:List=====================" ) println(r 01 .collect().mkString( "," )) println( "===================createDataMethod:makeRDD:List=====================" ) /* Array */ val rdd 02 = sc.makeRDD(Array( 1 , 2 , 3 , 4 , 5 , 6 )) val r 02 = rdd 02 .filter { x = > x < 5 } println( "===================createDataMethod:makeRDD:Array=====================" ) println(r 02 .collect().mkString( "," )) println( "===================createDataMethod:makeRDD:Array=====================" ) /* 使用parallelize创建RDD */ /* List */ val rdd 03 = sc.parallelize(List( 1 , 2 , 3 , 4 , 5 , 6 ), 1 ) val r 03 = rdd 03 .map { x = > x + 1 } println( "===================createDataMethod:parallelize:List=====================" ) println(r 03 .collect().mkString( "," )) println( "===================createDataMethod:parallelize:List=====================" ) /* Array */ val rdd 04 = sc.parallelize(List( 1 , 2 , 3 , 4 , 5 , 6 ), 1 ) val r 04 = rdd 04 .filter { x = > x > 3 } println( "===================createDataMethod:parallelize:Array=====================" ) println(r 04 .collect().mkString( "," )) println( "===================createDataMethod:parallelize:Array=====================" ) } /** * 创建Pair Map */ def createPairRDD() : Unit = { val rdd : RDD[(String,Int)] = sc.makeRDD(List(( "key01" , 1 ),( "key02" , 2 ),( "key03" , 3 ))) val r : RDD[String] = rdd.keys println( "===========================createPairRDD=================================" ) println(r.collect().mkString( "," )) println( "===========================createPairRDD=================================" ) } /** * 通过文件创建RDD * 文件数据: * key01,1,2.3 key02,5,3.7 key03,23,4.8 key04,12,3.9 key05,7,1.3 */ def createDataFromFile(path : String) : Unit = { val rdd : RDD[String] = sc.textFile(path, 1 ) val r : RDD[String] = rdd.flatMap { x = > x.split( "," ) } println( "=========================createDataFromFile==================================" ) println(r.collect().mkString( "," )) println( "=========================createDataFromFile==================================" ) } /** * 基本的RDD操作 */ def basicTransformRDD(path : String) : Unit = { val rddInt : RDD[Int] = sc.makeRDD(List( 1 , 2 , 3 , 4 , 5 , 6 , 2 , 5 , 1 )) val rddStr : RDD[String] = sc.parallelize(Array( "a" , "b" , "c" , "d" , "b" , "a" ), 1 ) val rddFile : RDD[String] = sc.textFile(path, 1 ) val rdd 01 : RDD[Int] = sc.makeRDD(List( 1 , 3 , 5 , 3 )) val rdd 02 : RDD[Int] = sc.makeRDD(List( 2 , 4 , 5 , 1 )) /* map操作 */ println( "======map操作======" ) println(rddInt.map(x = > x + 1 ).collect().mkString( "," )) println( "======map操作======" ) /* filter操作 */ println( "======filter操作======" ) println(rddInt.filter(x = > x > 4 ).collect().mkString( "," )) println( "======filter操作======" ) /* flatMap操作 */ println( "======flatMap操作======" ) println(rddFile.flatMap { x = > x.split( "," ) }.first()) println( "======flatMap操作======" ) /* distinct去重操作 */ println( "======distinct去重======" ) println(rddInt.distinct().collect().mkString( "," )) println(rddStr.distinct().collect().mkString( "," )) println( "======distinct去重======" ) /* union操作 */ println( "======union操作======" ) println(rdd 01 .union(rdd 02 ).collect().mkString( "," )) println( "======union操作======" ) /* intersection操作 */ println( "======intersection操作======" ) println(rdd 01 .intersection(rdd 02 ).collect().mkString( "," )) println( "======intersection操作======" ) /* subtract操作 */ println( "======subtract操作======" ) println(rdd 01 .subtract(rdd 02 ).collect().mkString( "," )) println( "======subtract操作======" ) /* cartesian操作 */ println( "======cartesian操作======" ) println(rdd 01 .cartesian(rdd 02 ).collect().mkString( "," )) println( "======cartesian操作======" ) } /** * 基本的RDD行动操作 */ def basicActionRDD() : Unit = { val rddInt : RDD[Int] = sc.makeRDD(List( 1 , 2 , 3 , 4 , 5 , 6 , 2 , 5 , 1 )) val rddStr : RDD[String] = sc.parallelize(Array( "a" , "b" , "c" , "d" , "b" , "a" ), 1 ) /* count操作 */ println( "======count操作======" ) println(rddInt.count()) println( "======count操作======" ) /* countByValue操作 */ println( "======countByValue操作======" ) println(rddInt.countByValue()) println( "======countByValue操作======" ) /* reduce操作 */ println( "======countByValue操作======" ) println(rddInt.reduce((x ,y) = > x + y)) println( "======countByValue操作======" ) /* fold操作 */ println( "======fold操作======" ) println(rddInt.fold( 0 )((x ,y) = > x + y)) println( "======fold操作======" ) /* aggregate操作 */ println( "======aggregate操作======" ) val res : (Int,Int) = rddInt.aggregate(( 0 , 0 ))((x,y) = > (x. _ 1 + x. _ 2 ,y),(x,y) = > (x. _ 1 + x. _ 2 ,y. _ 1 + y. _ 2 )) println(res. _ 1 + "," + res. _ 2 ) println( "======aggregate操作======" ) /* foeach操作 */ println( "======foeach操作======" ) println(rddStr.foreach { x = > println(x) }) println( "======foeach操作======" ) } def main(args : Array[String]) : Unit = { println(System.getenv( "HADOOP_HOME" )) createDataMethod() createPairRDD() createDataFromFile( "file:///D:/sparkdata.txt" ) basicTransformRDD( "file:///D:/sparkdata.txt" ) basicActionRDD() /*打印结果*/ /*D://hadoop ===================createDataMethod:makeRDD:List===================== 1,4,9,16,25,36 ===================createDataMethod:makeRDD:List===================== ===================createDataMethod:makeRDD:Array===================== 1,2,3,4 ===================createDataMethod:makeRDD:Array===================== ===================createDataMethod:parallelize:List===================== 2,3,4,5,6,7 ===================createDataMethod:parallelize:List===================== ===================createDataMethod:parallelize:Array===================== 4,5,6 ===================createDataMethod:parallelize:Array===================== ===========================createPairRDD================================= key01,key02,key03 ===========================createPairRDD================================= key01,1,2.3,key02,5,3.7,key03,23,4.8,key04,12,3.9,key05,7,1.3 =========================createDataFromFile================================== 2,3,4,5,6,7,3,6,2 ======map操作====== ======filter操作====== 5,6,5 ======filter操作====== ======flatMap操作====== key01 ======flatMap操作====== ======distinct去重====== 4,6,2,1,3,5 ======distinct去重====== ======union操作====== 1,3,5,3,2,4,5,1 ======union操作====== ======intersection操作====== 1,5 ======intersection操作====== ======subtract操作====== 3,3 ======subtract操作====== ======cartesian操作====== (1,2),(1,4),(3,2),(3,4),(1,5),(1,1),(3,5),(3,1),(5,2),(5,4),(3,2),(3,4),(5,5),(5,1),(3,5),(3,1) ======cartesian操作====== ======count操作====== 9 ======count操作====== ======countByValue操作====== Map(5 -> 2, 1 -> 2, 6 -> 1, 2 -> 2, 3 -> 1, 4 -> 1) ======countByValue操作====== ======countByValue操作====== 29 ======countByValue操作====== ======fold操作====== 29 ======fold操作====== ======aggregate操作====== 19,10 ======aggregate操作====== ======foeach操作====== a b c d b a ======foeach操作======*/ } } |
Spark执行时候我们需要构造一个SparkContenxt的环境变量,构造环境变量时候需要构造一个SparkConf对象,例如代码:setAppName("xtq").setMaster("local[2]")
appName就是spark任务名称,master为local[2]是指使用本地模式,启动2个线程完成spark任务。