1.前言
DataSet是Spark重要的数据结构之一拥有比RDD更高的性能,比DataFrame更灵活的操作方式,是Spark SQL的扩展,提供了额外的编译时类型检查。本文将深入介绍DataSet的使用。
从Spark2.0开始,DataFrame成为了DataSet的特例,即DataFrame是DataSet的特殊情况。DataFrame是操作Row对象的DataSet。当数据集可以被编码成SparkSQL并且在编译时知道类型信息的时候可以使用DataSet。DataSet是混合了DataFrame和RDD的强类型分布式集合。
2.DataSet与DataFrame、RDD之间的相互转换
//DataFrame转DataSet
def fromDF(df: DataFrame): Dataset[Student] = {
df.as[Student]
}
//DataSet转RDD
def toRDD(ds: Dataset[RawPanda]): RDD[RawPanda] = {
ds.rdd
}
//DataFrame转DataSet
def toDF(ds: Dataset[RawPanda]): DataFrame = {
ds.toDF()
}
在传统DataFrame上使用Dataset的原因之一是编译时强类型的。DataFrame具有运行时模式信息,但缺少有关模式的编译时信息。这种强类型在创建库时特别有用,因为您可以更清楚地指定输入的要求和返回类型。
3.深入理解Dataset API
Dataset API的主要优势之一是可以更轻松地与Scala和Java代码集成。这样既保留了DataFrame拥有更多信息的schema的优势,同时又可以像操作RDD一样灵活
3.1需求
下面通过一个简单的需求详细介绍如果灵活使用DataSet API。
需求:有一张学生表(id,name,age,score),求成绩大于80的学生的姓名及成绩
3.2类RDD编程
DataSet使用与RDD函数类似的函数签名如:filter,map,mapPartitions和flatMap等。
//DataSet的类RDD操作:
case class Stu2(name:String, id:Int, age:Int, score:Int)
def testDataSet(session: SparkSession): Unit = {
//用于隐式转换
import session.implicits._
//加载mysql数据库中的数据进行测试
val kTopic = session.read.format("jdbc")
.option("url", MY_URL)
.option("dbtable", MY_DBTABLE)
.option("driver", MYSQL_DRIVER)
.option("user", MY_USER)
.option("password", MY_PASSWD)
val wlTable = kTopic.load()
val dsStu2 = wlTable.as[Stu2] //将DataFrame转为Student类型的DataSet
//使用类RDD操作方式将成绩大于80的学生的姓名和成绩筛选出来
val likeRDD = dsStu2.filter(row => row.score > 80).map(row => (row.name, row.score))
likeRDD.show(false) //likeRDD类型是DataSet[(String,Int)]
}
3.3类DataFrame操作
上面是类RDD的操作,DataSet还有类DataFrame的关系型操作:
//DataSet的类DataFrame操作
case class Stu2(name:String, id:Int, age:Int, score:Int)
def testDataSet(session: SparkSession): Unit = {
import session.implicits._
val kTopic = session.read.format("jdbc")
.option("url", MY_URL)
.option("dbtable", MY_DBTABLE)
.option("driver", MYSQL_DRIVER)
.option("user", MY_USER)
.option("password", MY_PASSWD)
val wlTable = kTopic.load()
val dsStu2 = wlTable.as[Stu2]
//使用类RDD操作方式将成绩大于80的学生的姓名和成绩筛选出来,下面两行完全一样
val likeDF1 = dsStu2.select(col("name"),col("score")).where(col("score")>80)
val likeDF2 = dsStu2.select($"name",$"score" ).where($"score" > 80)
likeDF1.show() //likeDF1 ==>DataSet[Row]
likeDF2.show() //likeDF2 ==>DataSet[Row]
}
//类RDD和类DataFrame混编,likeRDD是上面类RDD编程中最后得到的变量,为了方便直接用了
likeRDD.orderBy(col("_2") )//likeRDD是用的类RDD编程。但是orderBy很显然是类DataFrame编程,这种情况可以
其实已经注意到了,上面两种方式可以得到相同的效果,但是变量的类型韩式不尽相同的。比如dsStu2.select(col("name"),col("score"))
虽然dsStu2是DataSet类型但是通过select函数以后变成了DataFrame。
即某些操作(例如select)同时具有类型化和未类型化的实现。如果您提供的是列而不是通过TypedColumn,将获得一个DataFrame而不是一个Dataset。
4.总结
DataSet是Spark编程模型中的核心概念之一,拥有极高的性能。同时DataSet用于比RDD更多的schema信息,比DataFrame更灵活的操作数据集方式,是开发Spark程序的首选。