简介
RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。RDD具有数据流模型的特点:自动容错、位置感知性调度和可伸缩性。RDD允许用户在执行多个查询时显式地将工作集缓存在内存中,后续的查询能够重用工作集,这极大地提升了查询速度。
RDD的属性
(1)一组分片(Partition),即数据集的基本组成单位。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目。
2)一个计算每个分区的函数。Spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。
(3)RDD之间的依赖关系。RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。
(4)一个Partitioner,即RDD的分片函数。当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。
(5)一个列表,存储存取每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。
WordCount粗图解RDD
其中hello.txt
编写代码
官网地址:https://spark.apache.org/docs/latest/rdd-programming-guide.html
引jar包
提取数据文件 链接:https://pan.baidu.com/s/1uu7rI_0L0g1kI9v-h3uB0w 提取码:w4yc
记得将数据文件里面的标题给去掉
代码
package com.jbit.test
import org.apache.hadoop.io.{LongWritable, Text}
import org.apache.hadoop.mapred.TextInputFormat
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object TestRdd {
def main(args: Array[String]): Unit = {
var spark=new SparkContext(new SparkConf()
.setMaster("local")
.setAppName("a"))
//Spark Scala读取GBK文件的方法
var data=spark.hadoopFile("data/A.csv",classOf[TextInputFormat],classOf[LongWritable],classOf[Text],1)
.map(p => new String(p._2.getBytes,0,p._2.getLength,"GBK"))
var data2=spark.hadoopFile("data/address.csv",classOf[TextInputFormat],classOf[LongWritable],classOf[Text],1)
.map(p => new String(p._2.getBytes,0,p._2.getLength,"UTF-8"))
var data3=spark.hadoopFile("data/taobao_persona.csv",classOf[TextInputFormat],classOf[LongWritable],classOf[Text],1)
.map(p => new String(p._2.getBytes,0,p._2.getLength,"UTF-8"))
//var data=spark.textFile("data/A.csv")
//transfer(spark,"data/A.csv").foreach(println(_))
//f1(data)
//f2(data)
//f3(data)
//f4(data,data2)
f5(data3)
}
//每个饭圈内的数量
def f1(data:RDD[String])={
//根据,号拆分然后得到下标为4(商圈名称),数量
val dataRDD=data
//根据,号拆分出每一行的数据
.map(line => {line.split(",")}
//使用filter进行过滤(因为每一行的数量可能并不是那么完整)
).filter( x => x.length>14)
//得到下标为4的标题的值做为键
.map(x => (x(4),1))
//打印出统计的饭圈键对值
//dataRDD.take(100).foreach(println(_))
//统计出每个饭圈内的饭店的数量
var result=dataRDD.countByKey()
//取前10条并且打印出来
result.take(10).foreach(println(_))
println("-----------------------------")
//排序
//第一个_代表this,也就是当前对象,然后.出它的第二个值
//这里的意思就是将result转换为数组然后依次比较出它里面最大的数据并进行排序
result.toArray.sortWith(_._2>_._2).take(10).foreach(println(_))
}
//人均消费前10名的商店
def f2(data:RDD[String])={
var dataRDD=data
.map(line => line.split(","))
.filter(x => x.length>14)
.map(x => {
//以商店名为键,然后因为人均价格里面带个元,所以以元拆分然后将数据转换成int类型
(x(0),x(2).split("元")(0).toInt)
}
)
//排序
//false为降序,true为升序
dataRDD.sortBy(_._2,false)
.take(10).foreach(println(_))
}
//计算每种参观种类总消费
def f3(data:RDD[String]): Unit ={
val dataRDD=data
.map(line => line.split(","))
.filter(x => x.length>14)
.map(
x =>
(x(3),x(2).replace("元","").toInt)
)
//根据key做出的处理(k1,v1)(k1,v2)
//相同key下的v1放入a,v2放入b,然后把结果存入a,下一个值存入b
//重复上面的操作,直到结束
//总的就是说相同key的value加起来
var result=dataRDD.reduceByKey(
(a,b) => {
println("a: "+a)
println("b: "+b)
a+b
}
)
//result.take(10).foreach(println(_))
//取商品的平均价格
var dataRDD2=data
.map(line => line.split(","))
.filter(x => x.length>14)
.map(
x =>
//将value换成一个集合
// (自助餐,(140,1))
(x(3),(x(2).replace("元","").toInt,1))
)
var result2=dataRDD2.reduceByKey(
(a,b) => {
//(价格,数量)
//(140,1)
//将价格和数量统计出来
(a._1+b._1,a._2+b._2)
}
).map(
x =>
//(键,(平均值,总价格,数量))
// (自助餐,(平均值,总价格,数量))
(x._1,x._2._1 / x._2._2,x._2._1,x._2._2)
)
//取10条结果出来且循环打印
//result2.take(10).foreach(println(_))
var dataRDD3=data
.map(line => line.split(","))
.filter(x => x.length>14)
.map(
x =>
//将value换成一个集合
// (自助餐,(140,1))
(x(3),FoodTemp(x(2).replace("元","").toInt,1))
)
//使用模式匹配来自出平均价格及随机抽取数据
var result3=dataRDD3.reduceByKey(
(a,b) => {
//(价格,数量)
//(140,1)
//将价格和数据都统计出来
FoodTemp(a.price+b.price,a.num+b.num)
}
).map(
x =>
//(键,(总价格,数量))
// (自助餐,(总价格,数量))
Food(x._1,x._2.price / x._2.num,x._2.price,x._2.num)
)
//result3.take(10).foreach(println(_))
//随机抽取
//false取出随机值后,不放入数据源
//true取出随机值后,放入数据源(可能会重复)
//0.1为比例
result3.sample(false,0.1).foreach(println(_))
}
// 获取饭店名称,以及所在的行政区
def f4(data: RDD[String],data2:RDD[String]): Unit ={
val fRDD=data.map(
line =>
line.split(",")
).filter(
x =>
x.length>14
).map(
x =>
//(id,店铺名称)
(x(13),x(0))
)
val addressRDD=data2.map(
x => {
var line=x.split(",")
//(id,行政区名称)
(line(0),line(4))
}
)
//键一致
fRDD.join(addressRDD)
.take(10)
.foreach(println(_))
}
//淘宝商品购买量占有查看量的百分比
def f5(data:RDD[String])={
var result=data.map(
x => {
val ts=x.split(",")
(ts(0),ts(1),ts(2))
}
).groupBy(
//将上面存的值设置哪个为主键
//得出来的数据例:(195233146,CompactBuffer((104742442,195233146,1)))
x => x._2
//判断哪个值是我们所需要的(1(浏览)和4(购买))
).mapValues(
x => {
//计算商品浏览次数
val a=x.count(
active => {
active._3== "1"
}
)
//计算商品购买次数
val b = x.count(
active => {
active._3 == "4"
}
)
(b.toDouble/a.toDouble)
//保留四位小数
//(b.toDouble/a.toDouble).formatted("%.4f")
}
)
//result.take(100).foreach(println(_))
//将数据保存到本地
result.saveAsTextFile("D:\\tao")
}
//Spark Scala读取GBK文件的方法
def transfer(sc: SparkContext,path:String):RDD[String]={
sc.hadoopFile(path,classOf[TextInputFormat],classOf[LongWritable],classOf[Text],1)
.map(p => new String(p._2.getBytes,0,p._2.getLength,"GBK"))
}
}
package com.jbit.test
/**
* 美食实体类
* @param name 美食种类名称
* @param avg 平均消费
* @param sum 人均消息总额
* @param num 餐饮数量
*/
case class Food(name:String,avg:Int,sum:Int,num:Int)
case class FoodTemp(price:Int,num:Int)
如果存储文件的时候报错
Caused by: java.io.IOException: (null) entry in command string: null chmod 0644 D:\tao\_temporary\0\_temporary\attempt_20200808162905_0010_m_000000_0\part-00000
可以去:https://github.com/steveloughran/winutils下载到
然后将此文件放到C:\Windows\System32中即可
读取文件的代码
package com.jbit.test
import org.apache.hadoop.io.{LongWritable, Text}
import org.apache.hadoop.mapred.TextInputFormat
import org.apache.spark.{SparkConf, SparkContext}
object MyRead {
def main(args: Array[String]): Unit = {
val spark=new SparkContext(new SparkConf().setMaster("local").setAppName("a"))
//指定文件
/*var data=spark.hadoopFile("D:\\tao\\part-00000",classOf[TextInputFormat],classOf[LongWritable],classOf[Text],1)
.map(p => new String(p._2.getBytes,0,p._2.getLength,"UTF-8"))*/
//指定目录下的所有文件
var data=spark.hadoopFile("D:\\tao\\*",classOf[TextInputFormat],classOf[LongWritable],classOf[Text],1)
.map(p => new String(p._2.getBytes,0,p._2.getLength,"UTF-8"))
data.take(10).foreach(println(_))
}
}
如果觉得上面不够过瘾,可以再写写下面的
package com.jbit.test
import org.apache.hadoop.io.{LongWritable, Text}
import org.apache.hadoop.mapred.TextInputFormat
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object RDDJob2 {
def main(args: Array[String]): Unit = {
var spark=new SparkContext(new SparkConf().setMaster("local").setAppName("a"))
var data=spark.hadoopFile("data/A.csv",classOf[TextInputFormat],classOf[LongWritable],classOf[Text],1)
.map(p => new String(p._2.getBytes,0,p._2.getLength,"GBK"))
f1(data)
var data2=spark.hadoopFile("data/address.csv",classOf[TextInputFormat],classOf[LongWritable],classOf[Text],1)
.map(p => new String(p._2.getBytes,0,p._2.getLength,"UTF-8"))
f2(data,data2)
//Spark Scala读取GBK文件的方法
var data3=spark.hadoopFile("data/taobao_persona.csv",classOf[TextInputFormat],classOf[LongWritable],classOf[Text],1)
.map(p => new String(p._2.getBytes,0,p._2.getLength,"GBK"))
f3(data3)
var data4=spark.hadoopFile("data/YYK.csv",classOf[TextInputFormat],classOf[LongWritable],classOf[Text],1)
.map(p => new String(p._2.getBytes,0,p._2.getLength,"UTF-8"))
f4(data4)
}
//广东美食中,食物类别人气前10名(人气是根据评论数量)
def f1(data:RDD[String]): Unit ={
println("--------------")
var result=data.map(
line =>
line.split(",")
).filter(
line =>
line.length>13
).map(
line =>
(line(3),line(1).toInt)
)
var resultReduce=result.reduceByKey(
(a,b) =>
a+b
)
resultReduce.sortBy(_._2,false).take(10).foreach(println(_))
}
//行政区美食数量排名
def f2(data:RDD[String],data2:RDD[String]): Unit ={
var result=data.map(
line =>
line.split(",")
).filter(
line =>
line.length>13
).map(
x =>
(x(13),x(0))
)
var result2=data2.map(
line =>
line.split(",")
).filter(
line =>
line.length>3
).map(
x =>
(x(0),x(4))
)
var resultJoin=result.join(result2)
resultJoin.map(
x=>
(x._2._2,1)
).reduceByKey(
(a,b) =>
a+b
).sortBy(_._2,false).foreach(println(_))
}
//淘宝数据集,每天用户访问数量,并得出前10名
def f3(data:RDD[String]): Unit ={
var result=data.map(
line =>
line.split(",")
).filter(
line =>
line.length>4
).map(
x =>
(x(5),x(1).toInt)
)
var resultReduce=result.reduceByKey(
(a,b) =>
a+b
)
resultReduce.sortBy(_._2,false).take(10).foreach(println(_))
}
//优依库数据集,购买种类前10名
def f4(data:RDD[String]): Unit ={
var result=data.map(
line =>
line.split(",")
).filter(
line =>
line.length>10
).map(
x =>
(x(6),x(9).toInt)
)
var resultReduce=result.reduceByKey(
(a,b) =>
a+b
)
resultReduce.sortBy(_._2,false).take(10).foreach(println(_))
}
}