spark计算模型RDD_1.0
(一)什么是RDD
弹性分布式数据集
基于内存
弹性的
自动容错的
内存迭代
(er)RDD的五大属性
-
1.一个分区列表
每个RDD都有很多个分区,分区里面才是真正的数据,spark的任务是以分区为单位的,一个分区后期就对应一个spark的task,也就是一个分区就对应一个线程
-
2.作用在每一个rdd分区中的函数
举例:val rdd2=rdd1.map(x=>(x,1))
-
3.RDD之间的依赖关系
一个rdd会依赖于其它多个rdd,后期spark任务的容错机制就是根据这个特性
-
4.(可选项) 一个Partitioner,即RDD的分区函数
对于kv类型的RDD才会有分区函数这个概念(必须要产生shuffle),不是kv类型的RDD分区函数是None
spark中有两种分区函数:
-
hashPartitioner(默认值),其本质key,hashcode % 分区数 = 分区号
-
RangePartitioner, 是基于一定的范围的分区函数
-
-
5.(可选项) 一个列表,存储每个Partition的优先位置(可选项)。
spark后期再进行任务分配的时候,优先考虑存有数据的worker节点来进行任务的计算
(三)创建RDD
三种方式
-
1.通过一个已经存在的scala集合或者数组去构建
scala> val rdd1=sc.parallelize(List(1,2,3,4)) rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:2
scala> val rdd2=sc.parallelize(Array(1,2,3,4)) rdd2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[1] at parallelize at <console>:24
-
2.通过加载外部的数据源去构建
scala> val rdd3=sc.textFile("/sparkdata/input/word.txt") rdd3: org.apache.spark.rdd.RDD[String] = /sparkdata/input/word.txt MapPartitionsRDD[3] at textFile at <console>:24
//对于文件来说指定最小分区数,取决于hadoop的分片规则
scala> val rdd3=sc.textFile("/sparkdata/input/word.txt",3)
-
3.通过一个rdd转换之后生成一个新的rdd
val rdd4=rdd3.flatMap(_.split(" ")) rdd4: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[4] at flatMap at <console>:26
val rdd5=rdd4.map((_,1)) rdd5: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[5] at map at <console>:28
(四)RDD编程API
RDD的算子分类
操作描述 | 操作说明 |
---|---|
transformation | 该操作是从已有的数据集创建新的数据集. |
action | 该操作主要是对数据集运行计算后,将运算得到的结果返回给驱动程序 |
Transformation
操作名称 | 操作描述 |
---|---|
map | 将RDD中的每一个元素传入自定义函数,获取一个新的元素,然后用新的元素组成新的RDD |
filter | 对RDD中的每个元素进行判断,如果返回true,则保留,返回false,则过滤掉 |
flatMap | 与map类似,但是对每个元素都可以返回一个或多个新元素 |
groupByKey | 根据key进行分组,每个key对应一个iterable |
reduceByKey | 对每个key对应的value进行reduce操作 |
sortByKey | 对每个key对应的value进行排序操作 |
join | 对两个包含<key,value>对的RDD进行join操作,每个key join上的pair,都会传入自定义函数进行处理 |
cogroup | 同join,但是每个key对应的iterable都会传入自定义函数来进行处理 |
Action
操作名称 | 操作描述 |
---|---|
reduce | 将RDD中的所有元素进行聚合操作.第一个和第二个元素聚合,值与第三个元素聚合,值与第四个元素聚合,以此类推. |
collect | 将RDD中所有元素获取到本地客户端 |
count | 获取RDD元素总数 |
take(n) | 获取RDD中前n个元素 |
saveAsTextFile | 将RDD元素保存到文件中,对每个元素调用toString()方法 |
countByKey | 对每个key对应的值进行count计数 |
foreach | 遍历RDD中的每个元素. |
(五)RDD常用的算子操作
进入spark-shell
[root@node01 ~]# spark-shell --master spark://node01:7077
练1:map filter
scala> val rdd1=sc.parallelize(List(3,4,5,2,7,4,3,8,10))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24
scala> val rdd2 = rdd1.map(_*10).sortBy(x=>x,true)
rdd2: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[12] at sortBy at <console>:26
scala> val rdd3=rdd2.filter(_>5)
rdd3: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[13] at filter at <console>:28
scala> rdd3.collect
res2: Array[Int] = Array(20, 30, 30, 40, 40, 50, 70, 80, 100)
scala>
练2:flatMap
为了看起来清楚 下面例子把很多返回删除了
scala> val rdd1=sc.parallelize(Array("a b c","d e f","h i j"))
scala> val rdd2=rdd1.flatMap(_.split(" "))
scala> rdd2.collect
res3: Array[String] = Array(a, b, c, d, e, f, h, i, j)
scala>
练3:并集、交集
scala>
//定义两个rdd集合
scala> val rdd1=sc.parallelize(List(5,6,4,3))
scala> val rdd2=sc.parallelize(List(1,2,3,4))
//求并集,去重
scala> val rdd3=rdd1.union(rdd2)
scala> rdd3.distinct.collect
res0: Array[Int] = Array(4, 1, 5, 6, 2, 3)
//求交集
scala> val rdd4=rdd1.intersection(rdd2)
scala> rdd4.collect
res1: Array[Int] = Array(4, 3)
scala>
练4:join groupByKey
scala>
//定义两个rdd列表
scala> val rdd1=sc.parallelize(List(("tom",1),("jerry",3),("kitty",2)))
scala> val rdd2=sc.parallelize(List(("jerry",2),("tom",1),("shuke",2)))
//join (类似于内连接)
scala> val rdd3 = rdd1.join(rdd2)
scala> rdd3.collect
res2: Array[(String, (Int, Int))] = Array((tom,(1,1)), (jerry,(3,2)))
//
scala> val rdd4=rdd1 union rdd2
scala> rdd4.collect
res3: Array[(String, Int)] = Array((tom,1), (jerry,3), (kitty,2), (jerry,2), (tom,1), (shuke,2))
//groupByKey
scala> val rdd5=rdd4.groupByKey
rdd5: org.apache.spark.rdd.RDD[(String, Iterable[Int])] = ShuffledRDD[18] at groupByKey at <console>:30
scala> rdd5.collect
res4: Array[(String, Iterable[Int])] = Array((tom,CompactBuffer(1, 1)), (jerry,CompactBuffer(3, 2)), (shuke,CompactBuffer(2)), (kitty,CompactBuffer(2)))
scala>
练5:cogroup
scala>
//定义两个rdd列表
scala> val rdd1=sc.parallelize(List(("tom",1),("tom",2),("jerry",3),("kitty",2)))
scala> val rdd2=sc.parallelize(List(("jerry",2),("tom",1),("jim",2)))
//cogroup
scala> val rdd3=rdd1.cogroup(rdd2)
rdd3: org.apache.spark.rdd.RDD[(String, (Iterable[Int], Iterable[Int]))] = MapPartitionsRDD[22] at cogroup at <console>:28
scala> rdd3.collect
res5: Array[(String, (Iterable[Int], Iterable[Int]))] = Array((jim,(CompactBuffer(),CompactBuffer(2))), (tom,(CompactBuffer(1, 2),CompactBuffer(1))), (jerry,(CompactBuffer(3),CompactBuffer(2))), (kitty,(CompactBuffer(2),CompactBuffer())))
scala>
注意groupByKey与cogroup的区别
结果对比
//groupByKey
Array[(String, Iterable[Int])] = Array((tom,CompactBuffer(1, 1)), (jerry,CompactBuffer(3, 2)), (shuke,CompactBuffer(2)), (kitty,CompactBuffer(2)))
//cogroup
Array[(String, (Iterable[Int], Iterable[Int]))] = Array((jim,(CompactBuffer(),CompactBuffer(2))), (tom,(CompactBuffer(1, 2),CompactBuffer(1))), (jerry,(CompactBuffer(3),CompactBuffer(2))), (kitty,(CompactBuffer(2),CompactBuffer())))
练6:reduce
scala>
scala> val rdd1=sc.parallelize(List(1,2,3,4,5))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[23] at parallelize at <console>:24
scala> val rdd2=rdd1.reduce(_+_) //第一个_表示累加结果
rdd2: Int = 15
scala>
练7:reduceByKey sortByKey
scala>
scala> val rdd1=sc.parallelize(List(("tom",1),("jerry",3),("kitty",2),("shuke",1)))
scala> val rdd2=sc.parallelize(List(("jerry",2),("tom",3),("shuke",2),("kitty",5)))
scala> val rdd3=rdd1.union(rdd2)
//按key进行聚合
scala> val rdd4=rdd3.reduceByKey(_+_)
rdd4: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[36] at reduceByKey at <console>:30
scala> rdd4.collect
res6: Array[(String, Int)] = Array((tom,4), (jerry,5), (shuke,3), (kitty,7))
//按value的降序排序
scala> val rdd5 = rdd4.map(t=>(t._2,t._1)).sortByKey(false).map(t=>(t._2,t._1))
rdd5: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[41] at map at <console>:32
scala> rdd5.collect
res7: Array[(String, Int)] = Array((kitty,7), (jerry,5), (tom,4), (shuke,3))
scala>
练8:repatition coalesce
//原来三个分区
scala> val rdd1=sc.parallelize(1 to 10,3)
//减少分区
scala> rdd1.repartition(2).partitions.size
res9: Int = 2
//coalesce
scala> val rdd3=rdd1.coalesce(1)
rdd3: org.apache.spark.rdd.RDD[Int] = CoalescedRDD[1] at coalesce at <console>:26
scala> rdd3.partitions.length
res3: Int = 1
scala>
(六)通过spark实现点击流日志分析案例
1.访问的PV
package cn.itcast.rdd
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object PV {
def main(args: Array[String]): Unit = {
//1.创建sparkConf
val sparkConf: SparkConf = new SparkConf().setAppName("PV").setMaster("local[2]")
//2.创建SparkCotext
val sc = new SparkContext(sparkConf)
sc.setLogLevel("warn")
//3.读取数据文件
val data: RDD[String] = sc.textFile("F:\\AAAA_HM大数据\\00-课件\\18_spark\\spark\\day02\\资料\\运营商日志\\access.log")
//4.统计PV //map---->每一行---->context.write(new Text("PV"),1) reduce -------> 汇总
val pv: Long = data.count()
println("PV:"+pv)
//5.关闭sc
sc.stop()
}
}
/*
...
00/00/00 00:50:50 INFO BlockManager: Initialized BlockManager: BlockManagerId(driver, 192.168.239.1, 10323, None)
PV:14622
*/
2.访问的UV
package cn.itcast.rdd
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
//todo:利用spark实现点击流日志数据分析---------UV
object UV {
def main(args: Array[String]): Unit = {
//1.创建SparkConf
val sparkConf: SparkConf = new SparkConf().setAppName("UV").setMaster("local[2]")
//2.创建SparkContext
val sc = new SparkContext(sparkConf)
//3.读取数据文件
val data: RDD[String] = sc.textFile("F:\\AAAA_HM大数据\\00-课件\\18_spark\\spark\\day02\\资料\\运营商日志\\access.log")
//4.切分每一行 获取ip地址
val ipRDD: RDD[String] = data.map(x=>x.split(" ")(0))
//5.ip地址去重
val distinctRDD: RDD[String] = ipRDD.distinct()
//6.统计UV
val uv: Long = distinctRDD.count()
println("UV:"+uv)
//7.关闭
sc.stop()
}
}
/*
...
00/00/00 00:28:38 INFO DAGScheduler: Job 0 finished: count at UV.scala:25, took 0.817432 s
UV:1051
00/00/00 00:28:38 INFO ...
*/
3.Top5
package cn.itcast.rdd
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
//todo:利用spark实现点击流日志数据分析---------TopN
object TopN {
def main(args: Array[String]): Unit = {
//1.创建SparkConf
val sparkConf: SparkConf = new SparkConf().setAppName("TopN").setMaster("local[2]")
//2.创建SparkContext
val sc = new SparkContext(sparkConf)
//3.读取数据文件
val data: RDD[String] = sc.textFile("F:\\AAAA_HM大数据\\00-课件\\18_spark\\spark\\day02\\资料\\运营商日志\\access.log")
//4.过滤出一些丢失的字段记录 保留正确的数据 获取URL
val url: RDD[String] = data.filter(x=>x.split(" ").length>10).map(x=>x.split(" ")(10))
//5.每个url记为1
val urlAndOne: RDD[(String, Int)] = url.map(x=>(x,1))
//6.相同url出现的1累加
val result: RDD[(String, Int)] = urlAndOne.reduceByKey(_+_)
//7.按照url出现的次序降序
val sortedRDD: RDD[(String, Int)] = result.sortBy(x=>x._2,false)
//8.取出url出现次数最多的前5位
val top5: Array[(String, Int)] = sortedRDD.take(5)
top5.foreach(x=>println(x))
//9.关闭
sc.stop()
}
}
/*
...
00/00/00 00:43:50 INFO DAGScheduler: Job 1 finished: take at TopN.scala:32, took 0.197640 s
("-",5206)
("http://blog.fens.me/category/hadoop-action/",547)
("http://blog.fens.me/",377)
("http://blog.fens.me/wp-admin/post.php?post=2445&action=edit&message=10",360)
("http://blog.fens.me/r-json-rjson/",274)
00/00/00 00:43:50 INFO SparkUI: Stopped Spark web UI at http://192.168.239.1:4040
...
*/