32.Spark RDD、DataFrame、DataSet区别和联系

1.简介

 

  •  在Spark中,DataFrame是一种以RDD为基础的分布式数据集,类似于传统数据库中的二维表格。DataFrame与RDD的主要区别在于,前者带有schema元信息,即DataFrame所表示的二维表数据集的每一列都带有名称和类型。这使得Spark SQL得以洞察更多的结构信息,从而对藏于DataFrame背后的数据源以及作用于DataFrame之上的变换进行了针对性的优化,最终达到大幅提升运行时效率的目标。反观RDD,由于无从得知所存数据元素的具体内部结构,Spark Core只能在stage层面进行简单、通用的流水线优化。
  •  左侧的RDD[Person]虽然以Person为类型参数,但Spark框架本身不了解Person类的内部结构。而右侧的DataFrame却提供了详细的结构信息,使得Spark SQL可以清楚地知道该数据集中包含哪些列,每列的名称和类型各是什么。DataFrame多了数据的结构信息,即schema。RDD是分布式的Java对象的集合。DataFrame是分布式的Row对象的集合。DataFrame除了提供了比RDD更丰富的算子以外,更重要的特点是提升执行效率、减少数据读取以及执行计划的优化,比如filter下推、裁剪等。

2.RDD

  • 优点:
  1. -编译时类型安全 
  2.  编译时就能检查出类型错误
  3. -面向对象的编程风格 
  4.  直接通过类名点的方式来操作数据
  • 缺点:
  1. -序列化和反序列化的性能开销 
  2.  无论是集群间的通信, 还是IO操作都需要对对象的结构和数据进行序列化和反序列化.
  3. -GC的性能开销 
  4.  频繁的创建和销毁对象, 势必会增加GC

3.DataFrame

DataFrame引入了schema和off-heap

schema : RDD每一行的数据, 结构都是一样的,这个结构就存储在schema中。 Spark通过schema就能够读懂数据, 因此在通信和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也不是面向对象风格的。

  • 创建DataFrame

  在Spark SQL中,开发者可以非常便捷地将各种内、外部的单机、分布式数据转换为DataFrame。以下Python示例代码充分体现了Spark SQL 1.3.0中DataFrame数据源的丰富多样和简单易用:

# 从Hive中的users表构造DataFrame  
users = sqlContext.table("users")  
  
# 加载S3上的JSON文件  
logs = sqlContext.load("s3n://path/to/data.json", "json")  
  
# 加载HDFS上的Parquet文件  
clicks = sqlContext.load("hdfs://path/to/data.parquet", "parquet")  
  
# 通过JDBC访问MySQL  
comments = sqlContext.jdbc("jdbc:mysql://localhost/comments", "user")  
  
# 将普通RDD转变为DataFrame  
rdd = sparkContext.textFile("article.txt") \  
                  .flatMap(lambda line: line.split()) \  
                  .map(lambda word: (word, 1)) \  
                  .reduceByKey(lambda a, b: a + b) \  
wordCounts = sqlContext.createDataFrame(rdd, ["word", "count"])  
  
# 将本地数据容器转变为DataFrame  
data = [("Alice", 21), ("Bob", 24)]  
people = sqlContext.createDataFrame(data, ["name", "age"])  
  
# 将Pandas DataFrame转变为Spark DataFrame(Python API特有功能)  
sparkDF = sqlContext.createDataFrame(pandasDF)  

  

  可见,从Hive表,到外部数据源API支持的各种数据源(JSON、Parquet、JDBC),再到RDD乃至各种本地数据集,都可以被方便快捷地加载、转换为DataFrame。这些功能也同样存在于Spark SQL的Scala API和Java API中。

  • 使用DataFrame

  和R、Pandas类似,Spark DataFrame也提供了一整套用于操纵数据的DSL。这些DSL在语义上与SQL关系查询非常相近(这也是Spark SQL能够为DataFrame提供无缝支持的重要原因之一)。以下是一组用户数据分析示例:

# 创建一个只包含"年轻"用户的DataFrame  
young = users.filter(users.age < 21)  
  
# 也可以使用Pandas风格的语法  
young = users[users.age < 21]  
  
# 将所有人的年龄加1  
young.select(young.name, young.age + 1)  
  
# 统计年轻用户中各性别人数  
young.groupBy("gender").count()  
  
# 将所有年轻用户与另一个名为logs的DataFrame联接起来  
young.join(logs, logs.userId == users.userId, "left_outer")  

  除DSL以外,我们当然也可以像以往一样,用SQL来处理DataFrame: 

young.registerTempTable("young")  
sqlContext.sql("SELECT count(*) FROM young")  

  最后,当数据分析逻辑编写完毕后,我们便可以将最终结果保存下来或展现出来: 

# 追加至HDFS上的Parquet文件  
young.save(path="hdfs://path/to/data.parquet",  
           source="parquet",  
           mode="append")  
  
# 覆写S3上的JSON文件  
young.save(path="s3n://path/to/data.json",  
           source="json",  
           mode="append")  
  
# 保存为SQL表  
young.saveAsTable(tableName="young", source="parquet" mode="overwrite")  
  
# 转换为Pandas DataFrame(Python API特有功能)  
pandasDF = young.toPandas()  
  
# 以表格形式打印输出  
young.show()  

4.DataSet

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

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

5.区别与联系

  • RDD和DataSet
  1. DataSet以Catalyst逻辑执行计划表示,并且数据以编码的二进制形式被存储,不需要反序列化就可以执行sorting、shuffle等操作。
  2. DataSet创立需要一个显式的Encoder,把对象序列化为二进制,可以把对象的scheme映射为Spark SQL类型,然而RDD依赖于运行时反射机制。
  3. DataSet比RDD性能要好很多。

 

  • DataFrame和DataSet
  1. Dataset可以认为是DataFrame的一个特例,主要区别是Dataset每一个record存储的是一个强类型值而不是一个Row。因此具有如下三个特点:
  2. DataSet可以在编译时检查类型
  3. DataSet是面向对象的编程接口。
  4. 后面版本DataFrame会继承DataSet,DataFrame是面向Spark SQL的接口。
  5. DataFrame和DataSet可以相互转化,df.as[ElementType]这样可以把DataFrame转化为DataSet,ds.toDF()这样可以把DataSet转化为DataFrame。
posted @ 2017-10-13 16:37  桃源仙居  阅读(345)  评论(0)    收藏  举报