RDD是Spark提供的最重要的抽象的概念,弹性的分布式数据集,它是一种有容错机制的特殊集合,可以分布在集群的节点上,以函数式编操作集合的方式,进行各种并行操作。Spark的RDD内置了各种函数操作,举个例子,我们编写wordcount案例,如果使用mapreduce进行编程,还是很复杂的,如果用RDD的话代码量大大的减少(scala编程一句话搞定),所以相对mapreduce来说单从编程上就简化了很多。但是同时也出现了一个问题,学习Scala、python、java语言,那么这个使用的成本以及门槛就会很高了对于不太懂开发的人(DBA)想要使用spark是比较困难的。
对于DataFrame这一概念最早是出现在R和Pandas里面的,R语言是非常适合做一些数据统计和分析的一些操作,但是它仅支持单机的处理,随着互联网的快速发展,单机处理的日志、数据必然是很有限的,而且现在的日志/数据量是越来越大,随着spark的不断壮大,在spark里面就出现了DataFrame的API(1.3版本出现的)。
官网对于DataFrame的解读。
A DataFrame is a Dataset organized into named columns
DataFrame是一个数据集,他会被按照列进行组织,并且这个列是被取了一个名称的。它在概念上等同于关系数据库中的表或R / Python中的数据框,但是进行了更丰富的优化。 DataFrame可以从各种来源构建而成(外部数据源),例如:结构化数据文件,Hive中的表格,外部数据库或现有的RDD。 DataFrame API可用于Scala,Java,Python和R.在Scala和Java中。 在Scala API中,DataFrame只是Dataset [Row]的类型别名。 而在Java API中,用户需要使用数据集来表示DataFrame。
总的来说DataFrame 是一个Dataset,那是一个Dataset又是啥子呢,先别着急,有个概念的认知。但是我们可以知道DataFrame 概念上等同于关系数据库中的表,看到表,我们肯定会想到,表中的一些字段,类型,值等等。而且还能通过各种数据源构建,瞬间感觉功能好强大啊,还有一句,可以通过现有的RDD获取也就是涉及到DataFrame和RDD之间的转化(后面进行详解)。
通过上面的介绍我们可以知道DataFrame和RDD都是分布式的数据集,但是DataFrame更像是一个传统的数据库里面的表,他除了数据之外还能够知道更多的信息,比如说列名、列值和列的属性,这一点就和hive很类似了,而且他也能够支持一些复杂的数据格式。从API应用的角度来说DataFrame提供的API他的层次更高,比RDD编程还要方便,学习的门槛更低。下面举个例子进行对比:
上图直观地体现了DataFrame和RDD的区别。左侧的RDD[Person]虽然以Person为类型参数,但Spark框架本身不了解Person类的内部结构。而右侧的DataFrame却提供了详细的结构信息,使得Spark SQL可以清楚地知道该数据集中包含哪些列,每列的名称和类型各是什么。DataFrame多了数据的结构信息,即schema。RDD是分布式的Java对象的集合。DataFrame是分布式的Row对象的集合。
sparksql执行的时候就能了解到更多的信息,sparksql的查询优化器(Catalyst)能够更好的优化,比如你只是想要那么字段,它仅仅只需要把那么那一列取出来就可以了,age和height根本就不需要去读取了,有了这些信息以后在编译的时候能够做更多的优化,比如filter下推、裁剪等。
使用RDD的方式如果你用java/Scala那你需要运行在jvm上,python就是python runtime,所以性能上会有所差别。
但是DataFrame底层采用同一个优化器,在性能上都是一样的。
Dataset是分布式数据集合。是Spark1.6中添加的新接口,它提供了RDD的优点以及Spark SQL优化执行引擎的优势。数据集可以从JVM对象构建,然后使用功能转换(map, flatMap, filter, 等)进行操作。数据集API可用于Scala和Java。 Python不支持数据集API。但由于Python的动态特性,数据集API的许多优点已经可用(即,您可以通过自然的row.columnName名称访问行的字段)。 R的情况类似。
Dataset to represent a DataFrame.
Dataset可以认为是DataFrame的一个特例,主要区别是Dataset每一个record存储的是一个强类型值而不是一个Row。因此具有如下三个特点:
Spark SQL支持将现有RDD转换为Datasets的两种不同方法。 第一种方法使用反射来推断包含特定类型对象的RDD的模式。这种基于反射的方法会导致更简洁的代码,并且在编写Spark应用程序时已经知道模式的情况下工作良好.第二种创建数据集的方法是通过编程接口,允许您构建模式,然后将其应用于现有的RDD。 虽然此方法更详细,但它允许您在直到运行时才知道列及其类型时才能构建数据集。
注意: 使用这种方式在scala2.10之前最多支持22个字段,scala2.11解决了这问题。如果想支持多个字段那就要使用下面的编程方式就不会受版本的影响
使用场景: 读取已有的文件,例如,存在于hdfs上的文件。
person.txt
1,20,Michael
2,30,Andy
3,19,ustin
4,45,Tom
import org.apache.spark.sql.SparkSession
object RDDToDataFrame {
def main(args: Array[String]): Unit = {
val spark=SparkSession.builder().appName("RDDToDataFrame").master("local[2]").getOrCreate()
//RDD
val personRDD=spark.sparkContext.textFile("E:/person.txt")
.map((_.split(","))).map(attributes => Person(attributes(0).toInt, attributes(1).trim.toInt,attributes(2)))
//导入隐式转换
import spark.implicits._
//RDD->DataFrame
val personDF=personRDD.toDF()
personDF.show()
spark.stop()
}
case class Person(id: Int, age: Int,name: String)
}
结果:
+---+---+-------+
| id|age| name|
+---+---+-------+
| 1| 20|Michael|
| 2| 30| Andy|
| 3| 19| ustin|
| 4| 45| Tom|
+---+---+-------+
/只查询name
//personDF.select("name").show()
//filter
//personDF.filter(personDF.col("id")>2).show()
通过SQL方式进行查询
//用sparkSQL的方式来查询数据
personDF.createTempView("person")
spark.sql("show tables").show()
spark.sql("select * from person").show()
小结:
步骤:首先定义一个case class 使用sparkContext.textFile读取文件得到一个RDD,把RDD转化为DF
import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.types.{StringType, StructField, StructType}
object RDDToDataFrame1 {
def main(args: Array[String]): Unit = {
val spark=SparkSession.builder().appName("RDDToDataFrame1").master("local[2]").getOrCreate()
// Create an RDD
val personRDD=spark.sparkContext.textFile("E:/person.txt")
// The schema is encoded in a string
val schemalString="id,age,name"
// Generate the schema based on the string of schema
val fields=schemalString.split(",").map(filedName=>StructField(filedName,StringType,nullable =true ))
val schema=StructType(fields)
// Convert records of the RDD (person) to Rows
val rowRDD=personRDD.map(x=>x.split(",")).map(attributes=>Row(attributes(0),attributes(1),attributes(2)))
// Apply the schema to the RDD
val personDF = spark.createDataFrame(rowRDD, schema)
//print nice tree
personDF.printSchema()
//show
personDF.show()
}
}
root
|-- id: string (nullable = true)
|-- age: string (nullable = true)
|-- name: string (nullable = true)
+---+---+-------+
| id|age| name|
+---+---+-------+
| 1| 20|Michael|
| 2| 30| Andy|
| 3| 19| ustin|
| 4| 45| Tom|
+---+---+-------+
总结:
DataFrame和RDD互操作的两个方式:
1、反射:case class 前提:事先需要知道你的字段、字段类型
2、编程:Row 如果第一种情况不能满足你的要求(事先不知道列)
3、选型:优先考虑第一种