RDD、DataFrame和Dataset的关系

RDD、DataFrame和Dataset的关系

DataFrame是特殊的RDD(他相当于RDD+schema,即RDD+表信息),可以将他看成数据库中的一张数据表,但是只知道这个"表"中的各个字段,不知道各个字段的数据类型。
Dataset是DataFrame的父类,当Dataset中存储Row(Row是一个类型,跟Car、Person这些的类型一样,所有的表结构信息我都用Row来表示)时,两者等价(Dataset[Row]=DataFrame)。

RDD、DataFrame和Dataset的优缺点

RDD优缺点

优点:
编译时类型安全:编译时就能检查出类型错误;
面向对象的编程风格:直接通过对象调用方法的形式来操作数据

缺点:
序列化和反序列化的性能开销大:无论是集群的通信,还是IO操作,都需要对对象的结构和数据进行序列化和反序列化。
GC(垃圾回收)的性能开销大:频繁的创建和销毁对象,增加了GC的负担。

DataFrame的优缺点

优点:DataFrame通过引入schema (即数据的结构信息)和off-heap(不在堆里面的内存,使用操作系统上的内存), Spark通过schame就能够读懂数据, 因此在通信和IO时就只需要序列化和反序列化数据, 而结构的部分就可以省略了;通过off-heap引入,可以快速的操作数据,避免大量的GC。

缺点:DataFrame不是类型安全的, API也不是面向对象风格的。

Dataset兼容了RDD和DataFrame的优点,使用在以后的spark中,Dataset将会逐步代替RDD和DataFrame的使用。

RDD、DataFrame和Dataset的相互转换

RDD转换DataFrame

通过反射推断Schema
这种方式首先是定义样例类Person, 然后通过将rdd与该样例类关联(通过样例类创建schema,case class的参数名称会被利用反射机制作为列名) 生成最终的RDD, 最后该RDD调用toDF方法转换为DataFrame.

val boyRDD: RDD[Boy] = lines.map(line => {
      val fields = line.split(",")
      val id = fields(0).toLong
      val name = fields(1)
      val age = fields(2).toInt
      val fv = fields(3).toDouble
      Boy(id, name, age, fv)
    })
//必须导入该隐式转换,否则,无法对将RDD转换成DataFrame
 import sqlContext.implicits._
    val bdf: DataFrame = boyRDD.toDF
case class Boy(id: Long, name: String, age: Int, fv: Double)

除此之外,还可以使用元组来取代样例类,直接让RDD调用toDF方法,将RDD转换成DataFrame,但是在调用toDF方法时,需要为元组中的每个数据指定列名。

val boyRDD: RDD[Boy] = lines.map(line => {
      val fields = line.split(",")
      val id = fields(0).toLong
      val name = fields(1)
      val age = fields(2).toInt
      val fv = fields(3).toDouble
      (id, name, age, fv)
    })
//必须导入该隐式转换,否则,无法对将RDD转换成DataFrame
 import sqlContext.implicits._
    val bdf: DataFrame = boyRDD.toDF("id","name","age","fv")

注:最好使用样例类的方式,元组的话,在将DataFrame转换成Dataset时,会抛出异常。

通过StructType直接指定Schema
这种方式首先是将rdd与row类型关联得到rowRDD, 然后定义structType类型数据用来指定schema. 最后通过sparkSession调用createDataFrame方法(指定rowRDD以及schema两个参数)来得到DataFrame.

 //将数据进行整理
    val rowRDD: RDD[Row] = lines.map(line => {
      val fields = line.split(",")
      val id = fields(0).toLong
      val name = fields(1)
      val age = fields(2).toInt
      val fv = fields(3).toDouble
      Row(id, name, age, fv)
    })
  /**
      * 结果类型,其实就是表头,用于描述DataFrame,
      * 第一个参数表示表的列名,
      * 第二个参数表示数据的数据类型,
      * 第三个参数表示数据是否能为"null",true表示可以为"null"
      */
    val schema: StructType = StructType(List(
      StructField("id", LongType, true),
      StructField("name", StringType, true),
      StructField("age", IntegerType, true),
      StructField("fv", DoubleType, true)
    ))
//创建DataFrame
    val df: DataFrame = session.createDataFrame(rowRDD, schema)

RDD转换Dataset

通过反射推断Schema
这种方式首先是定义样例类Person, 然后通过将rdd与该样例类关联(通过样例类创建schema,case class的参数名称会被利用反射机制作为列名) 生成最终的RDD, 最后该RDD调用toDS方法转换为Dataset.

//将数据进行整理
    val boyRDD = lines.map(line => {
      val fields = line.split(",")
      val id = fields(0).toLong
      val name = fields(1)
      val age = fields(2).toInt
      val fv = fields(3).toDouble
      Boy(id, name, age, fv)
    })
    import sqlContext.implicits._
    val dS: Dataset[Boy] = boyRDD.toDS
    case class Boy(id: Long, name: String, age: Int, fv: Double)

注:不能使用元组来代替样例类。

DataFrame转换Dataset

直接调用DataFrame的as[样例类]方法即可将DataFrame转换成Dataset;其中"样例类"表示将RDD转换成DataFrame时的样例类,如果不是样例类,即使转换成功后,在运行时,仍然会报错。

DataFrame转换RDD

直接调用DataFrame的rdd方法即可将DataFrame转换成rdd;并且该RDD存储的数据类型是Row类型,与DataFrame中存储的样例类无关。

Dataset转换RDD

直接调用Dataset的rdd方法即可将Dataset转换成rdd;并且该rdd中存储的数据类型是Datase存储的样例类。

Dataset转换DataFrame

直接调用Dataset的toDF即可将Dataset转换成DataFrame

猜你喜欢

转载自blog.csdn.net/weixin_38613375/article/details/89500160