大数据-94 Spark核心三剑客:RDD、DataFrame、Dataset与SparkSession全面解析

点一下关注吧!!!非常感谢!!持续更新!!!

🚀 AI篇持续更新中!(长期更新)

AI炼丹日志-31- 千呼万唤始出来 GPT-5 发布!"快的模型 + 深度思考模型 + 实时路由",持续打造实用AI工具指南!📐🤖

💻 Java篇正式开启!(300篇)

目前2025年09月08日更新到: Java-118 深入浅出 MySQL ShardingSphere 分片剖析:SQL 支持范围、限制与优化实践 MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈! 大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解

章节内容

上节完成的内容如下:

  • SparkSQL介绍
  • SparkSQL特点
  • SparkSQL数据抽象
  • SparkSQL数据类型

SparkSession

在 Spark2.0 之前

  • SQLContext 是创建 DataFrame 和 执行SQL的入口
  • HiveContext 通过HiveSQL语句操作Hive数据,兼Hive操作,HiveContext继承自SQLContext

在 Spark2.0 后

  • 这些入口点统一到了SparkSession,SparkSession封装了SQLContext及HiveContext
  • 实现了SQLContext即HiveContext所有功能
  • 通过SparkSession可以获取到SparkContext

RDD(Resilient Distributed Dataset,弹性分布式数据集)

RDD 是 Spark 的核心数据抽象,它代表一个不可变的、可分区、可并行计算的数据集合。RDD 是 Spark 实现高效分布式计算的基础,它能够跨集群节点自动分区,并提供丰富的操作接口。

核心特点详解

1. 不可变性

RDD 一旦创建就不能被修改。任何转换操作(如 map、filter)都会生成一个新的 RDD,而不是修改原始 RDD。这种设计带来以下优势:

  • 简化容错处理
  • 支持并行计算
  • 便于数据共享
  • 示例:对一个存储用户数据的 RDD 进行过滤操作会生成仅包含符合条件用户的新 RDD

2. 弹性容错能力

RDD 通过以下机制实现容错:

  • 血统(Lineage):记录 RDD 的生成过程(即从哪些父 RDD 通过什么操作得到)
  • 检查点(Checkpointing):可选的持久化机制,将 RDD 数据保存到稳定存储
  • 分区(Partitioning):数据被划分为多个分区,每个分区可以在不同节点上并行计算

当节点故障时,Spark 可以根据血统信息重新计算丢失的分区,而不需要复制整个数据集。

3. 分布式计算

RDD 支持两种分区方式:

  1. 哈希分区:根据键的哈希值分配数据
  2. 范围分区:根据键的范围分配数据

典型的分区数量通常是集群CPU核心数的2-4倍,以充分利用并行计算能力。

4. 延迟计算机制

RDD 采用惰性求值策略,包括两类操作:

  • 转换操作(Transformations):如 map、filter、reduceByKey 等,只记录计算逻辑
  • 行动操作(Actions):如 count、collect、saveAsTextFile 等,触发实际计算

这种设计允许 Spark 优化整个计算流程,减少不必要的数据传输。

5. 类型安全与API特点

虽然 RDD 是类型化的(如 RDD[String]),但它的 API 是松散类型的:

  • 编译时不会检查类型匹配
  • 运行时才会发现类型错误
  • 与DataFrame/Dataset相比,缺少查询优化能力

创建RDD的三种主要方式

  1. 从集合创建:使用 SparkContext.parallelize()
scala 复制代码
val data = Array(1, 2, 3, 4, 5)
val rdd = sc.parallelize(data)
  1. 从外部存储系统:如 HDFS、S3、本地文件系统
scala 复制代码
val rdd = sc.textFile("hdfs://path/to/file")
  1. 从现有RDD转换:通过转换操作生成新RDD
scala 复制代码
val newRdd = rdd.map(x => x * 2)

典型应用场景

  • 迭代式算法(如机器学习)
  • 交互式数据挖掘
  • ETL流水线处理
  • 流式数据处理(通过微批处理实现)
  • 图计算(结合GraphX库)

DataFrame

DataFrame 是一种基于 RDD 的分布式数据集,它具有命名的列。它是 Spark SQL 中的核心数据结构,为处理结构化数据提供了高效的接口。

主要特点

1. 结构化数据

DataFrame 是一个二维表格数据结构,具有以下特点:

  • 由行(Row)和列(Column)组成
  • 每列都有明确的名称和数据类型
  • 类似于关系数据库中的表或 Pandas 的 DataFrame
  • 支持多种数据源,包括 JSON、CSV、Parquet、JDBC 等

示例:一个表示员工信息的DataFrame可能包含以下列:

  • id: Integer
  • name: String
  • department: String
  • salary: Double

2. 优化引擎

DataFrame 受益于 Spark SQL 引擎的优化功能:

  • Catalyst 优化器 :自动执行查询优化
    • 谓词下推
    • 列剪裁
    • 常量折叠
    • 连接重排序
  • Tungsten 执行引擎:提供高效的二进制内存表示
  • 自动生成高效的执行计划
  • 比直接使用 RDD 性能更高

3. 丰富的 API

DataFrame 提供多种操作方式:

  • SQL 风格操作

    python 复制代码
    df.select("name", "salary").filter(df.salary > 5000)
  • DSL 操作

    python 复制代码
    df.groupBy("department").agg({"salary": "avg"})
  • 支持各种转换操作:

    • 过滤(filter)
    • 投影(select)
    • 聚合(groupBy/agg)
    • 排序(orderBy)
    • 连接(join)
    • 窗口函数(window)

4. 类型安全

与 RDD 相比,DataFrame 的类型检查特性:

  • 编译时不进行类型检查
  • 运行时才会验证数据类型
  • 可能导致运行时错误
  • 可使用 Dataset API 获得编译时类型检查

应用场景

DataFrame 特别适合以下场景:

  1. 结构化数据处理
  2. 数据仓库查询
  3. ETL 流程
  4. 数据分析与可视化
  5. 机器学习数据准备

性能优势

通过 DataFrame API 编写代码通常比直接使用 RDD API 更高效,因为:

  1. 优化器可以理解操作语义
  2. 减少数据序列化/反序列化开销
  3. 采用列式存储格式
  4. 启用代码生成技术

DataSet

DataSet 是 Spark 1.6 引入的一个新的数据抽象,它结合了 RDD 的强类型优势和 DataFrame 的优化能力。

特点:

  • 类型安全:DataSet 是强类型的,它利用编译时类型检查,确保在编译时检测类型错误。
  • 优化和性能:DataSet 受益于 Catalyst 优化器和 Tungsten 执行引擎,提供与 DataFrame 相同的优化能力,同时保留了类型安全性。
  • 更丰富的 API:DataSet 提供了 RDD 的大部分 API,如 map、filter 等,同时也支持 SQL 查询。
  • 统一 API:DataSet API 统一了 RDD 和 DataFrame,提供了一种更具表现力和安全性的编程模型。

DataFrame & Dataset 创建

不要刻意区分: DF & DS,DF是一种特殊的DS:ds.transformation => ds

由 Range 生成 Dataset

在 spark-shell 中进行测试

scala 复制代码
val numDS = spark.range(5, 100, 5)
// orderBy 转换操作 
numDS.orderBy(desc("id")).show(5)
// 统计信息
numDS.describe().show
// 显示 Schema 信息
numDS.printSchema
// 使用RDD执行同样的操作
numDS.rdd.map(_.toInt).stats
// 检查分区数
numDS.rdd.getNumPartitions

运行测试的过程如下图所示:

有集合生成Dataset

Dataset = RDD[case class],在 spark-shell 中进行测试

scala 复制代码
case class Person(name: String, age: Int, height: Int)

// 注意 Seq 中元素的类型
val seq1 = Seq(Person("Jack", 28, 184), Person("Tom", 10, 144), Person("Andy", 16, 165))

val ds1 = spark.createDataset(seq1)
ds1.printSchema
ds1.show

执行的结果: 再来一个测试:

scala 复制代码
val seq2 = Seq(("Jack", 28, 184), ("Tom", 10, 144), ("Andy", 16, 165))
val ds2 = spark.createDataset(seq2)
ds2.printSchema
ds2.show

执行的结果:

由集合生成DataFrame

DataFrame = RDD[Row] + Schema 继续进行测试:

scala 复制代码
val lst = List(("Jack", 28, 184), ("Tom", 10, 144), ("Andy", 16, 165))
val df1 = spark.createDataFrame(lst).withColumnRenamed("_1", "name1").withColumnRenamed("_2", "age1").withColumnRenamed("_3", "height1")
df1.orderBy("age1").show(10)

执行的结果如下图所示:

RDD转成DataFrame

DataFrame = RDD[Row] + Schema

scala 复制代码
val arr = Array(("Jack", 28, 184), ("Tom", 10, 144), ("Andy", 16, 165))
val rdd1 = sc.makeRDD(arr).map(f => Row(f._1, f._2, f._3))

val schema = StructType(
  StructField("name", StringType, false) ::
  StructField("age", IntegerType, false) ::
  StructField("height", IntegerType, false) ::
  Nil
)

val schema1 = (new StructType).add("name", "string", false).add("age", "int", false).add("height", "int", false)
val rddToDF = spark.createDataFrame(rdd1, schema)
rddToDF.orderBy(desc("name")).show(false)

执行的结果如下图:

RDD转Dataset

Dataset = RDD[case class] DataFrame = RDD[Row] + Schema

scala 复制代码
val arr = Array(("Jack", 28, 184), ("Tom", 10, 144), ("Andy", 16, 165))
val rdd1 = sc.makeRDD(arr)
val ds2 = spark.createDataset(rdd1)
ds2.show(10)

执行的结果如下图:

从文件创建DataFrame

CSV文件

我们生成了一个CSV文件,大致内容如下:

运行测试

scala 复制代码
val df1 = spark.read.csv("/opt/wzk/data/people1.csv")
df1.printSchema()
df1.show()

运行结果如下图所示:

三者转换

Spark SQL 提供了一个领域特定语言(DSL)以方便操作结构化数据,核心思想还是SQL,仅仅是一个语法问题。

RDD 与 DataFrame 之间的转换

RDD 转换为 DataFrame

将 RDD 转换为 DataFrame 需要提供数据的模式信息。通常你会使用 toDF() 方法将 RDD 转换为 DataFrame。 这里有两种主要方法:

  • 使用隐式转换:需要导入 spark.implicits._,这允许你在不显式提供模式的情况下将常见的 RDD(如元组)转换为 DataFrame。
  • 使用 StructType 定义模式:如果 RDD 的数据结构比较复杂,或者你需要精确控制 DataFrame 的模式,可以使用 StructType 和 Row。

DataFrame 转换为 RDD:

  • 将 DataFrame 转换为 RDD 非常简单,只需调用 rdd 方法即可

DataFrame 与 DataSet 之间的转换

DataFrame 转换为 DataSet

  • DataFrame 是无类型的,而 DataSet 是类型化的。为了将 DataFrame 转换为 DataSet,你需要定义一个对应的数据类型(通常是一个 case class)并使用 as[T] 方法

DataSet 转换为 DataFrame

  • 将 DataSet 转换为 DataFrame 非常简单,只需调用 toDF() 方法即可

RDD 与 DataSet 之间的转换

RDD 转换为 DataSet

  • 将 RDD 转换为 DataSet 需要将 RDD 的元素类型与 DataSet 的类型一致。与将 RDD 转换为 DataFrame 类似,通常使用隐式转换或显式提供模式信息

DataSet 转换为 RDD

  • DataSet 本质上是类型化的 RDD,因此转换为 RDD 非常直接,只需调用 rdd 方法

最终汇总

  • RDD 转换为 DataFrame:使用 toDF(),或使用 createDataFrame() 提供模式。
  • DataFrame 转换为 RDD:使用 rdd 方法,转换后元素类型为 Row。
  • DataFrame 转换为 DataSet:使用 as[T] 方法,需提供对应的 case class。
  • DataSet 转换为 DataFrame:使用 toDF() 方法。
  • RDD 转换为 DataSet:使用 toDS(),需提供对应的 case class。
  • DataSet 转换为 RDD:使用 rdd 方法。
相关推荐
大象席地抽烟3 分钟前
使用 Ollama 本地模型与 Spring AI Alibaba
后端
程序员小假6 分钟前
SQL 语句左连接右连接内连接如何使用,区别是什么?
java·后端
isNotNullX7 分钟前
怎么用数据仓库来进行数据治理?
大数据·数据库·数据仓库·数据治理
小坏讲微服务8 分钟前
Spring Cloud Alibaba Gateway 集成 Redis 限流的完整配置
数据库·redis·分布式·后端·spring cloud·架构·gateway
方圆想当图灵1 小时前
Nacos 源码深度畅游:Nacos 配置同步详解(下)
分布式·后端·github
HitpointNetSuite1 小时前
连锁餐饮行业ERP系统如何选择?
大数据·数据库·oracle·netsuite·erp
方圆想当图灵1 小时前
Nacos 源码深度畅游:Nacos 配置同步详解(上)
分布式·后端·github
小羊失眠啦.1 小时前
用 Rust 实现高性能并发下载器:从原理到实战
开发语言·后端·rust
zskj_zhyl1 小时前
科技助老与智慧养老的国家级政策与地方实践探索
大数据·人工智能·科技
YangYang9YangYan1 小时前
职业本科发展路径与规划指南
大数据·人工智能·学习·数据分析