Spark RDD、DataFrame和DataSet的区别

转自:http://blog.csdn.NET/wo334499/article/details/51689549 


RDD

优点:

  1. 编译时类型安全 
    编译时就能检查出类型错误
  2. 面向对象的编程风格 
    直接通过类名点的方式来操作数据

缺点:

  1. 序列化和反序列化的性能开销 
    无论是集群间的通信, 还是IO操作都需要对对象的结构和数据进行序列化和反序列化.
  2. GC的性能开销 
    频繁的创建和销毁对象, 势必会增加GC
import org.apache.spark.sql.SQLContext
import org.apache.spark.{SparkConf, SparkContext}

object Run {
  def main(args: Array[String]) {
    val conf = new SparkConf().setAppName("test").setMaster("local")
    val sc = new SparkContext(conf)
    sc.setLogLevel("WARN")
    val sqlContext = new SQLContext(sc)

    /**
      * id      age
      * 1       30
      * 2       29
      * 3       21
      */
    case class Person(id: Int, age: Int)
    val idAgeRDDPerson = sc.parallelize(Array(Person(1, 30), Person(2, 29), Person(3, 21)))

    // 优点1
    // idAge.filter(_.age > "") // 编译时报错, int不能跟String比

    // 优点2
    idAgeRDDPerson.filter(_.age > 25) // 直接操作一个个的person对象
  }
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

DataFrame

DataFrame引入了schema和off-heap

  • schema : RDD每一行的数据, 结构都是一样的. 这个结构就存储在schema中.Spark通过schame就能够读懂数据, 因此在通信和IO时就只需要序列化和反序列化数据, 而结构的部分就可以省略了.

  • off-heap : 意味着JVM堆以外的内存, 这些内存直接受操作系统管理(而不是JVM)。Spark能够以二进制的形式序列化数据(不包括结构)到off-heap中, 当要操作数据时, 就直接操作off-heap内存. 由于Spark理解schema, 所以知道该如何操作.

off-heap就像地盘, schema就像地图, Spark有地图又有自己地盘了, 就可以自己说了算了, 不再受JVM的限制, 也就不再收GC的困扰了.

通过schema和off-heap, DataFrame解决了RDD的缺点, 但是却丢了RDD的优点. DataFrame不是类型安全的, API也不是面向对象风格的.

import org.apache.spark.sql.types.{DataTypes, StructField, StructType}
import org.apache.spark.sql.{Row, SQLContext}
import org.apache.spark.{SparkConf, SparkContext}

object Run {
  def main(args: Array[String]) {
    val conf = new SparkConf().setAppName("test").setMaster("local")
    val sc = new SparkContext(conf)
    sc.setLogLevel("WARN")
    val sqlContext = new SQLContext(sc)
    /**
      * id      age
      * 1       30
      * 2       29
      * 3       21
      */
    val idAgeRDDRow = sc.parallelize(Array(Row(1, 30), Row(2, 29), Row(4, 21)))

    val schema = StructType(Array(StructField("id", DataTypes.IntegerType), StructField("age", DataTypes.IntegerType)))

    val idAgeDF = sqlContext.createDataFrame(idAgeRDDRow, schema)
    // API不是面向对象的
    idAgeDF.filter(idAgeDF.col("age") > 25) 
    // 不会报错, DataFrame不是编译时类型安全的
    idAgeDF.filter(idAgeDF.col("age") > "") 
  }
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

DataSet

DataSet结合了RDD和DataFrame的优点, 并带来的一个新的概念Encoder

当序列化数据时, Encoder产生字节码与off-heap进行交互, 能够达到按需访问数据的效果, 而不用反序列化整个对象. Spark还没有提供自定义Encoder的API, 但是未来会加入.

下面看DataFrame和DataSet在2.0.0-preview中的实现

下面这段代码, 在1.6.x中创建的是DataFrame

<code
 class="hljs mathematica has-numbering" style="display: block; padding: 
0px; color: inherit; box-sizing: border-box; font-family: 'Source Code 
Pro', monospace;font-size:undefined; white-space: pre; border-radius: 
0px; word-wrap: normal; background: transparent;">// 
上文DataFrame示例中提取出来的
val idAgeRDDRow = sc.parallelize(<span class="hljs-keyword" 
style="color: rgb(0, 0, 136); box-sizing: 
border-box;">Array</span>(<span class="hljs-keyword" 
style="color: rgb(0, 0, 136); box-sizing: 
border-box;">Row</span>(<span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">1</span>, <span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">30</span>), <span class="hljs-keyword" 
style="color: rgb(0, 0, 136); box-sizing: 
border-box;">Row</span>(<span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">2</span>, <span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">29</span>), <span class="hljs-keyword" 
style="color: rgb(0, 0, 136); box-sizing: 
border-box;">Row</span>(<span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">4</span>, <span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">21</span>)))

val schema = StructType(<span class="hljs-keyword" style="color: 
rgb(0, 0, 136); box-sizing: 
border-box;">Array</span>(StructField(<span 
class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: 
border-box;">"id"</span>, DataTypes.IntegerType), 
StructField(<span class="hljs-string" style="color: rgb(0, 136, 0); 
box-sizing: border-box;">"age"</span>, DataTypes.IntegerType)))

val idAgeDF = sqlContext.createDataFrame(idAgeRDDRow, 
schema)</code><ul class="pre-numbering" style="box-sizing: 
border-box; position: absolute; width: 50px; top: 0px; left: 0px; 
margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; 
border-right-style: solid; border-right-color: rgb(221, 221, 221); 
list-style: none; text-align: right; background-color: rgb(238, 238, 
238);"><li style="box-sizing: border-box; padding: 0px 
5px;">1</li><li style="box-sizing: border-box; padding: 0px 
5px;">2</li><li style="box-sizing: border-box; padding: 0px 
5px;">3</li><li style="box-sizing: border-box; padding: 0px 
5px;">4</li><li style="box-sizing: border-box; padding: 0px 
5px;">5</li><li style="box-sizing: border-box; padding: 0px 
5px;">6</li></ul>

但是同样的代码在2.0.0-preview中, 创建的虽然还叫DataFrame

<code
 class="hljs python has-numbering" style="display: block; padding: 0px; 
color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', 
monospace;font-size:undefined; white-space: pre; border-radius: 0px; 
word-wrap: normal; background: transparent;">// 
sqlContext.createDataFrame(idAgeRDDRow, schema) 方法的实现, 返回值依然是DataFrame
<span class="hljs-function" style="box-sizing: 
border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 
136); box-sizing: border-box;">def</span> <span 
class="hljs-title" style="box-sizing: 
border-box;">createDataFrame</span><span class="hljs-params"
 style="color: rgb(102, 0, 102); box-sizing: border-box;">(rowRDD: 
RDD[Row], schema: StructType)</span>:</span> DataFrame = {
sparkSession.createDataFrame(rowRDD, schema)
}</code><ul class="pre-numbering" style="box-sizing: 
border-box; position: absolute; width: 50px; top: 0px; left: 0px; 
margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; 
border-right-style: solid; border-right-color: rgb(221, 221, 221); 
list-style: none; text-align: right; background-color: rgb(238, 238, 
238);"><li style="box-sizing: border-box; padding: 0px 
5px;">1</li><li style="box-sizing: border-box; padding: 0px 
5px;">2</li><li style="box-sizing: border-box; padding: 0px 
5px;">3</li><li style="box-sizing: border-box; padding: 0px 
5px;">4</li></ul>

但是其实却是DataSet, 因为DataFrame被声明为Dataset[Row]

<code
 class="hljs r has-numbering" style="display: block; padding: 0px; 
color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', 
monospace;font-size:undefined; white-space: pre; border-radius: 0px; 
word-wrap: normal; background: transparent;">package object sql {
  // <span class="hljs-keyword" style="color: rgb(0, 0, 136); 
box-sizing: border-box;">...</span>省略了不相关的代码

  type DataFrame = Dataset[Row]
}</code><ul class="pre-numbering" style="box-sizing: 
border-box; position: absolute; width: 50px; top: 0px; left: 0px; 
margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; 
border-right-style: solid; border-right-color: rgb(221, 221, 221); 
list-style: none; text-align: right; background-color: rgb(238, 238, 
238);"><li style="box-sizing: border-box; padding: 0px 
5px;">1</li><li style="box-sizing: border-box; padding: 0px 
5px;">2</li><li style="box-sizing: border-box; padding: 0px 
5px;">3</li><li style="box-sizing: border-box; padding: 0px 
5px;">4</li><li style="box-sizing: border-box; padding: 0px 
5px;">5</li></ul>

因此当我们从1.6.x迁移到2.0.0的时候, 无需任何修改就直接用上了DataSet.

下面是一段DataSet的示例代码

<code
 class="hljs scala has-numbering" style="display: block; padding: 0px; 
color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', 
monospace;font-size:undefined; white-space: pre; border-radius: 0px; 
word-wrap: normal; background: transparent;"><span 
class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: 
border-box;">import</span> 
org.apache.spark.sql.types.{DataTypes, StructField, StructType}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: 
border-box;">import</span> org.apache.spark.sql.{Row, 
SQLContext}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: 
border-box;">import</span> org.apache.spark.{SparkConf, 
SparkContext}

<span class="hljs-class" style="box-sizing: border-box;"><span 
class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: 
border-box;">object</span> <span class="hljs-title" 
style="box-sizing: border-box; color: rgb(102, 0, 
102);">Test</span> {</span>
  <span class="hljs-keyword" style="color: rgb(0, 0, 136); 
box-sizing: border-box;">def</span> main(args: Array[String]) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); 
box-sizing: border-box;">val</span> conf = <span 
class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: 
border-box;">new</span> SparkConf().setAppName(<span 
class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: 
border-box;">"test"</span>).setMaster(<span 
class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: 
border-box;">"local"</span>) <span class="hljs-comment" 
style="color: rgb(136, 0, 0); box-sizing: border-box;">// 
调试的时候一定不要用local[*]</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); 
box-sizing: border-box;">val</span> sc = <span 
class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: 
border-box;">new</span> SparkContext(conf)
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); 
box-sizing: border-box;">val</span> sqlContext = <span 
class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: 
border-box;">new</span> SQLContext(sc)
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); 
box-sizing: border-box;">import</span> sqlContext.implicits._

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); 
box-sizing: border-box;">val</span> idAgeRDDRow = 
sc.parallelize(Array(Row(<span class="hljs-number" style="color: 
rgb(0, 102, 102); box-sizing: border-box;">1</span>, <span 
class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: 
border-box;">30</span>), Row(<span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">2</span>, <span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">29</span>), Row(<span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">4</span>, <span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">21</span>)))

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); 
box-sizing: border-box;">val</span> schema = 
StructType(Array(StructField(<span class="hljs-string" style="color: 
rgb(0, 136, 0); box-sizing: border-box;">"id"</span>, 
DataTypes.IntegerType), StructField(<span class="hljs-string" 
style="color: rgb(0, 136, 0); box-sizing: 
border-box;">"age"</span>, DataTypes.IntegerType)))

    <span class="hljs-comment" style="color: rgb(136, 0, 0); 
box-sizing: border-box;">// 在2.0.0-preview中这行代码创建出的DataFrame, 
其实是DataSet[Row]</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); 
box-sizing: border-box;">val</span> idAgeDS = 
sqlContext.createDataFrame(idAgeRDDRow, schema)

    <span class="hljs-comment" style="color: rgb(136, 0, 0); 
box-sizing: border-box;">// 在2.0.0-preview中, 还不支持自定的Encoder, Row类型不行,
 自定义的bean也不行</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); 
box-sizing: border-box;">// 
官方文档也有写通过bean创建Dataset的例子,但是我运行时并不能成功</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); 
box-sizing: border-box;">// 所以目前需要用创建DataFrame的方法, 
来创建DataSet[Row]</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); 
box-sizing: border-box;">// 
sqlContext.createDataset(idAgeRDDRow)</span>

    <span class="hljs-comment" style="color: rgb(136, 0, 0); 
box-sizing: border-box;">// 目前支持String, Integer, 
Long等类型直接创建Dataset</span>
    Seq(<span class="hljs-number" style="color: rgb(0, 102, 102); 
box-sizing: border-box;">1</span>, <span class="hljs-number"
 style="color: rgb(0, 102, 102); box-sizing: 
border-box;">2</span>, <span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">3</span>).toDS().show()
    sqlContext.createDataset(sc.parallelize(Array(<span 
class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: 
border-box;">1</span>, <span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">2</span>, <span class="hljs-number" 
style="color: rgb(0, 102, 102); box-sizing: 
border-box;">3</span>))).show()
  }
}</code>

参考

Introducing Apache Spark Datasets
APACHE SPARK: RDD, DATAFRAME OR DATASET?
RDD、DataFrame和DataSet的区别
Spark 2.0.0-preview 官方文档



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值