RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。这里的弹性指的是RDD可以根据当前情况自动进行内存和硬盘存储的转换
简单点讲就是spark中对数据的一个封装,把数据封装进对象,容易操作
在spark中所有的计算都是围绕着RDD操作的,每个 RDD 都被分为多个分区,这些分区运行在集群中的不同节点上,并且RDD也可以缓存到内存中
RDD支持两种操作:转化操作和行动(计算)操作。
RDD 的转化操作是把原来的RDD换成新的,这样做主要是为了把数据转换成方便计算的格式,例如之前的学习中我们计算过文件中的单词数量
数据1
java~HTML~spark
java
scala
在上面文件中不方便计算机计算数量,所以这时候需要转换,转化成下面的格式
数据2
java,1
HTML,1
spark,1
java,1
scala,1
也就是说我们把封装了数据1的RDD转换成了封装了数据2的RDD。这就是转换这一类操作的作用。
新的RDD则包含了如何从其他RDDs衍生所必需的信息,所以说RDDs之间是有依赖关系的,简单点说刚才封装数据2的RDD由数据1RDD衍生过来的,可以简单理解为数据2RDD是数据1RDD的孩子,体内有数据1RDD的DNA,这个就是RDD中的血缘。
基于RDDs之间的依赖,RDDs会形成一个有向无环图DAG,该DAG描述了整个流式计算的流程,实际执行的时候,RDD是通过血缘关系(Lineage)一气呵成的,即使出现数据分区丢失,也可以通过血缘关系重建分区,
简单讲就是根据RDD中的血缘关系,工作的时候一个家族的RDD都要去执行工作,工作期间有的RDD跑了(数据丢失),也没关系,可以根据家族中其他成员再次找到他
如果说转换方法是把数据转换成合适的计算方式,那么行动操作就是最后的计算方式。
我们使用上面的方式把数据转换成下面的输出格式
java,1
html,1
。。。。
而计算操作就是把每个值的1都加起来,得到和后展示或保存。
注意:Spark采用惰性计算模式(懒执行),RDD只有第一次在一个行动操作中用到时,才会真正计算运行。
首先介绍下环境背景
object Food {
def main(args: Array[String]): Unit = {
// 创建spark上下文
val spark = new SparkContext("local","food")
// 读取文件中数据 A.csv文件是广东美食数据集
val data = spark.textFile("data/A.csv")
// 打印前10条
data.take(10).foreach(println(_))
}
}
后续代码都是基于此时的data变量编写
map(func)
经过函数转换后变成新的RDD,
例如在当前数据集中我只需要名称,评论数,人均价格,类别,商圈,此时就可以使用map
val result = data.map(
x => {
val arr = x.split(",")
val mony = arr(2).split("元")
(arr(0),arr(1),mony(0).toInt,arr(3),arr(4))
}
)
result.take(10).foreach(println(_))
打印结果》》》
(极炙·台灣精致炭火烤肉,3551,211,日本料理,天河城/体育中心)
(大滷爺(正佳店),151,54,粤菜,天河城/体育中心)
(白天鹅宾馆·玉堂春暖餐厅,2018,312,粤菜,沙面)
(Mr.Fish鱼鲜生海鲜放题(高德置地冬广场店),7703,354,自助餐,高德置地/花城汇)
......
mapPartitions(func)
同上,不同的是在分区上进行,函数Iterator,假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区
由于直接处理的分区中的所有数据,当数据量庞大的时候回造成内存溢出
val result = data.mapPartitions(
x => {
var list = List();
while (x.hasNext){
val arr = x.next().split(",")
list :+ arr(3)
}
list.iterator // 注意返回的是迭代器类型
}
)
mapPartitionsWithIndex(func)
类似于mapPartitions,但func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是(Int, Interator[T]) => Iterator[U]
flatMap(func)
类似map,结果是数据的话会把数据中每一个值取出作为单个的值,返回多个值
val r1 = data.map(_.split(","))
.take(10)
.foreach(println(_))
打印》》》
[Ljava.lang.String;@53a665ad
[Ljava.lang.String;@2c0b4c83
[Ljava.lang.String;@78525ef9
......
val r2 = data.flatMap(_.split(","))
.take(10)
.foreach(println(_))
打印》》》
极炙·台灣精致炭火烤肉
3551
211元
日本料理
......
filter(func)
主要用于过滤,匿名函数返回值是true,则返回,为false则不返回数据
val result = data.map(
x => {
val arr = x.split(",")
val mony = arr(2).split("元")
(arr(0),arr(1),mony(0).toInt,arr(3),arr(4))
}
).filter(
x => {
x._4 == "日本料理"
}
)
result.take(10).foreach(println(_))
sample(withReplacement, fraction, seed)
采样变换根据给定的随机种子,从RDD中随机地按指定比例选一部分记录,创建新的RDD
withReplacement : Boolean , True数据复制出去的,下次取值还是从元数据取,可能重复;False取出后删除原值,不会重复
fraction : Double, 在0~1之间的一个浮点值,表示要采样的记录在全体记录中的比例
seed :随机种子
val result = data.sample(false,0.5)
result.take(10).foreach(println(_))
distinct()
去除重复的数据
sortBy(func)
按照func对数据进行处理,然后把结果排序,第二个为true为升序,为false为降序
// 饭店最贵前10名
val result = data.map(
x => {
val arr = x.split(",")
val mony = arr(2).split("元")
(arr(0),arr(1),mony(0).toInt,arr(3),arr(4))
}
)
result.sortBy(-_._3)
.take(10)
.foreach(println(_))
sortByKey([ascending], [numTasks])
根据key排序
union(otherDataset)
对源RDD和参数RDD求并集后返回一个新的RDD 不去重
subtract (otherDataset)
去除两个RDD中相同的元素,不同的RDD将保留下来
intersection(otherDataset)
对源RDD和参数RDD求交集后返回一个新的RDD
join(otherDataset, [numTasks])
在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD
首先说明下数据address.csv是美食店铺对应的地址
val addressRDD = spark.textFile("data/address.csv")
addressRDD.take(10).foreach(println(_))
打印》》》
店铺ID,公共交通,纬度,经度,行政区名称
id10002222,,23.081253,113.328349,海珠区
id100412754,,23.095611,113.272257,海珠区
id101488702,,23.09487246,113.2727256,海珠区
.......
val foodRDD = footerData.map(
line => line.split(",")
).filter(line => line.length>13)
.map(line => (line(13),line(0)))
val addressRDD = addressData.map(
line => {
val adds = line.split(",")
(adds(0),adds(4))
}
)
foodRDD.join(addressRDD).take(10)foreach(println(_))
cogroup(otherDataset, [numTasks])
在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable,Iterable))类型的RDD
groupBy()
接收一个函数,这个函数返回的值作为key,然后通过这个key来对里面的元素进行分组
groupByKey()
groupByKey也是对每个key进行操作,但只生成一个序列。
collect()
数组的形式返回数据集的所有元素
reduce(func)
每次传入两个参数通过fun 的到一个返回值,该返回值继续与后面的值进行调用fun,直到所有的数据计算完成,最后返回一个计算结果
data.reduce(
(a,b) => {
println(a)
println(b)
("和",a._2 + b._2)
}
)
两个参数分别代表RDD中的两个记录,返回值被RDD用来进行递归计算,其中a表示上一个数据
reduceByKey(func, [numTasks])
在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,reduce任务的个数可以通过第二个可选的参数来设置。
countByKey()
针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数
count()
返回元素数量