- 是Dataframe API的一个扩展,是Spark最新的数据抽象
- 用户友好的API风格,既具有类型安全检查也具有Dataframe的查询优化特性。
- Dataset支持编解码器,当需要访问非堆上的数据时可以避免反序列化整个对象,提高了效率。
- 样例类被用来在Dataset中定义数据的结构信息,样例类中每个属性的名称直接映射到DataSet中的字段名称。
- Dataframe是Dataset的特列,DataFrame=Dataset[Row] ,所以可以通过as方法将Dataframe转换为Dataset。Row是一个类型,跟Car、Person这些的类型一样,所有的表结构信息我都用Row来表示。
- DataSet是强类型的。比如可以有Dataset[Car],Dataset[Person]。
DataFrame只是知道字段,但是不知道字段的类型,所以在执行这些操作的时候是没办法在编译的时候检查是否类型失败的,比如你可以对一个String进行减法操作,在执行的时候才报错,而DataSet不仅仅知道字段,而且知道字段类型,所以有更严格的错误检查。就跟JSON对象和类对象之间的类比。
RDD让我们能够决定怎么做,而DataFrame和DataSet让我们决定做什么,控制的粒度不一样。
Dataset是一个分布式的数据集。Dataset是Spark 1.6开始新引入的一个接口,它结合了RDD API的很多优点(包括强类型,支持lambda表达式等),以及Spark SQL的优点(优化后的执行引擎)。Dataset可以通过JVM对象来构造,然后通过transformation类算子(map,flatMap,filter等)来进行操作。Scala和Java的API中支持Dataset,但是Python不支持Dataset API。不过因为Python语言本身的天然动态特性,Dataset API的不少feature本身就已经具备了(比如可以通过row.columnName来直接获取某一行的某个字段)。R语言的情况跟Python也很类似。
Dataset:typed(强类型)操作,Dataset与RDD比较类似,但是非常重要的一点不同是,RDD的序列化机制是基于Java序列化机制或者是Kryo的,而Dataset的序列化机制基于一种特殊的Encoder,来将对象进行高效序列化,以进行高性能处理或者是通过网络进行传输。Dataset除了Encoder,也同时支持Java序列化机制,但是encoder的特点在于动态的代码生成,同时提供一种特殊的数据格式,来让spark不将对象进行反序列化,即可直接基于二进制数据执行一些常见的操作,比如filter、sort、hash等。
1、创建Dataset
Dataset是具有强类型的数据集合,需要提供对应的类型信息。创建方式和DataFrame的创建类似。
1.1 创建Dataset第一种方式:借助隐式转换函数toDS从Scala集合或者RDD中创建Dataset
/**
* 创建Dataset第一种方式:借助隐式转换函数toDS从Scala集合或者RDD中创建Dataset
* 需要导入sparkSession对象定义好的隐式转换内容import sparkSession.implicits._
* 通过隐式转换toDS创建Dataset
* 集合或者RDD中是什么数据类型,创建的Dataset就是什么数据类型
* Dataset的列名和数据类型是有关系的,如果是集合,随机生成;如果是Bean类,列名就是Bean中属性名
*/
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{Dataset, SparkSession}
import scala.beans.BeanProperty
case class Teacher(@BeanProperty var name:String, @BeanProperty var age:Int)
object ImplicitDataset {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("StorageDemo")
val sparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
import sparkSession.implicits._
val seq:Seq[Teacher] = Array(Teacher("cy",32),Teacher("hr",23),Teacher("sq",33))
val dataset:Dataset[Teacher] = seq.toDS()
val teacher:Teacher = dataset.first()
println(teacher)
val name = teacher.getName
println(name)
dataset.show()
val rdd:RDD[Teacher] = sparkSession.sparkContext.makeRDD(seq)
val dataset1:Dataset[Teacher] = rdd.toDS()
dataset1.show()
}
}
1.2 创建Dataset的第二种方式:借助SparkSession对象createDataset从Scala、Java集合或者RDD中创建Dataset
/**
* 创建Dataset的第二种方式:借助SparkSession对象createDataset从Scala、Java集合或者RDD中创建Dataset
* 需要导入sparkSession对象定义好的隐式转换内容import sparkSession.implicits._
* 通过createDataset数据集,底层使用到函数的柯里化 需要传递第二个值是一个隐式参数,需要定义一个隐式变量给隐式参数传递值
* 隐式变量不需要我们定义,在SparkSession中全部给我们定义好了。隐式变量是一个编码器Encoder变量
*
* createDataset(RDD[T])
* createDataset(Seq[T])
* createDataset(util.List[T])
* Dataset的列名是和传入的集合类型或者元组类型或者是普通类型,那么列名随机生成;如果是Bean类,列名就是Bean中属性名
*/
import org.apache.spark.SparkConf
import org.apache.spark.sql.{Dataset, SparkSession}
object createDataset_Dataset {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("createDataset_Dataset")
val sparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
import sparkSession.implicits._ // createDataset第二个参数需要隐式变量参数值
// createDataset需要一个隐式变量值Encoder[Teacher]
// val dataset:Dataset[Teacher] = sparkSession.createDataset(Array(Teacher("cy", 32), Teacher("hr", 23), Teacher("sq", 33)))
val dataset:Dataset[(String,Int)] = sparkSession.createDataset(Array(("cy", 32), ("hr", 23), ("sq", 33)))
dataset.show()
}
}
1.3 创建Dataset的第三种方式:从外部文件加载
/**
* 创建Dataset的第三种方式:从外部文件加载sparkSession.read.options().textFile()
* 需要导入sparkSession对象定义好的隐式转换内容import sparkSession.implicits._
* 通过外部存储文件创建Dataset dataset只支持纯文本文件
* 虽然说Dataset不支持其他格式的结构化文件 ,但是我们可以曲线救国,先把结构化文件创建成DataFrame,然后把DataFrame转换成Dataset
*/
import org.apache.spark.SparkConf
import org.apache.spark.sql.{Dataset, SparkSession}
object FileCreateDataset {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("FileCreateDataset")
val sparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
val map = Map("mode" -> "FAILFAST", "inferSchema" -> "true")
val dataset:Dataset[String] = sparkSession.read.options(map).textFile("hdfs://node1:9000/student.txt")
dataset.show()
import sparkSession.implicits._
val dataset1:Dataset[Teacher] = dataset.map((line: String) => {
val array = line.split(" ")
Teacher(array(0), array(1).toInt)
})
dataset1.show()
}
}
1.4 Dataset的第四种创建方式:从DataFrame转换而来
/**
* * Dataset的第四种创建方式:从DataFrame转换而来
* 使用Dataset相关算子进行转换的来 都需要引入sparksession的隐式转换内容
* map算子 将原有的Dataset的每一行数据进行转换 得到一个新的数据类型 就是新的Dataset的类型
* flatMap算子 将原有的Dataset的每一行数据进行压扁操作 得到一个集合数据类型 集合数据就是新的Dataset的类型
* DataFrame中SQL操作算子都返回是一个Dataset结果集,除了select和selectExpr
*/
import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}
object SuanziTransDataset {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("FileCreateDataset")
val sparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
import sparkSession.implicits._
val seq: Seq[(String, Int)] = Array(("zs", 32), ("ls", 23), ("ww", 32), ("ml", 35))
val dataFrame:DataFrame = sparkSession.createDataFrame(seq)
val dataset:Dataset[Teacher] = dataFrame.map(row => {
Teacher(row.getAs[String](0), row.getAs[Int](1))
})
dataset.show()
val dataFrame2 = dataset.where("name = 'zs'").select("*")
dataFrame2.show()
val dataFrame1:DataFrame = sparkSession.read.text("hdfs://node1:9000/wc.txt")
dataFrame1.show()
val dataset1:Dataset[String] = dataFrame1.flatMap(row => {
val line = row.getAs[String](0)
line.split(" ")
})
dataset1.show()
}
}
2、Dataset的保存操作示例
以结构化文件的格式保存数据、保存到Hive数据表存储目录、保存到JDBC
DataFrame/Dataset.write.mode().cvs/text/json/parquet/orc/jdbc/saveAsTable
import org.apache.spark.SparkConf
import org.apache.spark.sql.{Dataset, SaveMode, SparkSession}
/**
* Dataset中保存操作
*/
object TestStorageDemo {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("StorageDemo")
val sparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
import sparkSession.implicits._
val dataset:Dataset[(String,Int)] = sparkSession.createDataset(Array(("zs", 32), ("ls", 23), ("ww", 32), ("ml", 35)))
val dataset1:Dataset[String] = dataset.map(tuple2 => {
tuple2._1 + " " + tuple2._2
})
dataset1.write.mode(SaveMode.Overwrite).text("hdfs://node1:9000/sparksql/test_text")
}
}