RDD
RDD(Resilient Distribute Datasets)弹性分布式数据集,是spark的核心概念,表示分布式存储的只读集合,可以基于RDD进行数据的转换操作(map,filter等)
Spark通过RDD的抽象概念,实现了分区计算存储、DAG、依赖等编程接口
rdd介绍:https://www.infoq.cn/article/spark-core-rdd
RDD vs DataFrame
以People为例,有id和name两个字段
case class People(id: Int, name: String)
val ps = Seq(People(1, "mwf"), People(2, "zqr"))
//peopleRDD的类型为RDD[Person]
val peopleRdd = spark.sparkContext.parallelize(ps)
peopleRdd.foreach( f => {
//f类型为People
println(s"${f.id} -- ${f.name}")
})
import spark.implicits._
//peopleDF的类型为DataFrame
val peopleDF = peopleRdd.toDF()
peopleDF.foreach( f => {
//f类型为Row
println(s"${f.getAs("id")} -- ${f.getAs("name")}")
})
可以看到RDD相当于是People类的集合,而DataFrame相当于是Row类型的集合
作为使用者,在操作RDD时候,我们可以在编译时明了的了解到People的字段,进行操作;而操作DataFrame时,其封装了row类型,我们要通过row.get()来获得字段,进行操作。
最明显的区别就是,使用RDD可以直接通过点字段的方式来操作(f.id),而DataFrame则不行。
但是DataFrame比RDD多了数据的结构类型,通过Row对象可以获得schema(row.schema),DataFrame基于SparkSQL引擎,可以进行优化,提高执行效率,减少数据读取
RDD优点:
-
编译时类型安全
-
编译时就能检查出类型错误
-
面向对象的编程风格
-
直接通过类名点的方式来操作数据
RDD缺点:
- 序列化和反序列化的性能开销
无论是集群间的通信, 还是IO操作都需要对对象的结构和数据进行序列化和反序列化. - GC的性能开销
频繁的创建和销毁对象, 势必会增加GC
DataFrame:
DataFrame引入了schema和off-heap
schema : RDD每一行的数据, 结构都是一样的,这个结构就存储在schema中。 Spark通过schema就能够读懂数据, 因此在通信和IO时就只需要序列化和反序列化数据, 而结构的部分就可以省略了。
off-heap : 意味着JVM堆以外的内存, 这些内存直接受操作系统管理(而不是JVM)。Spark能够以二进制的形式序列化数据(不包括结构)到off-heap中, 当要操作数据时,就直接操作off-heap内存。由于Spark理解schema,所以知道该如何操作。
DataSet
DataSet结合了RDD和DataFrame的优点,并带来的一个新的概念Encoder。
当序列化数据时,Encoder产生字节码与off-heap进行交互,能够达到按需访问数据的效果,而不用反序列化整个对象
RDD和DataSet
DataSet以Catalyst逻辑执行计划表示,并且数据以编码的二进制形式被存储,不需要反序列化就可以执行sorting、shuffle等操作
DataSet创立需要一个显式的Encoder,把对象序列化为二进制,可以把对象的scheme映射为Spark SQL类型,然而RDD依赖于运行时反射机制
DataSet比RDD性能要好很多
DataFrame和DataSet
DataFrame看做是DataSet的一个特例
Dataset每一个record存储的是一个强类型值而不是一个Row
DataSet可以在编译时检查类型
DataSet是面向对象的编程接口。
后面版本DataFrame会继承DataSet,DataFrame是面向Spark SQL的接口。
DataFrame和DataSet可以相互转化,df.as[ElementType]这样可以把DataFrame转化为DataSet,ds.toDF()这样可以把DataSet转化为DataFrame。
三者的相互转换
// 通过集合创建rdd
// 调用parallelize会把集合中的数据拷贝到集群中,形成分布式的数据集合(RDD)
val arr = Array("1,mwf","2,zqr")
val rdd = spark.sparkContext.parallelize(arr)
// 可以指定将集合切为几个分区
val rdd1 = spark.sparkContext.parallelize(arr, 2)
rdd.foreach(println(_))
rdd1.foreach(println(_))
//通过文本创建RDD
val textRdd = spark.sparkContext.textFile("hdfs://hadoop01:9000/demo/words.txt")
textRdd.flatMap(_.split(","))
.map((_, 1))
.reduceByKey(_+_)
// 通过RDD[Row]和schema(StructType)创建dataframe
// 创建schema(StructType)
val schema = new StructType()
.add("id", IntegerType)
.add("name", StringType)
val schema1 = StructType(
Array(
StructField("id", IntegerType, true),
StructField("name", StringType, false)
))
val rowRdd = rdd.map(_.split(","))
.map( x => Row(x(0).toInt, x(1)))
spark.createDataFrame(rowRdd, schema)
//通过隐式转换的方式创建dataframe
//会隐式转换为Row类型
import spark.implicits._
val df = rdd.map(_.split(","))
.map( x => People(x(0).toInt, x(1)))
.toDF()
df.show
//从DF中取值
df.map( row => {
//通过下标取
row.get(0)
//通过字段名取
row.getAs("id")
})
//DataFrame转为DataSet
val ds = df.as[People]
val seq = Seq(People(1, "mwf"))
val peopleDF = seq.toDF()
peopleDF.show()
peopleDF.map( x => {
x.getAs("id")
})
val peopleDs = seq.toDS()
peopleDs.show()
peopleDs.map( x => {
x.id
})
val peoDs = spark.createDataset(seq)
peoDs.rdd
peopleDF.rdd
参考地址
https://blog.csdn.net/u013063153/article/details/53317627
慢慢积累,总是好的