Spark SQL 核心知识详解:DataFrame、DSL 与数据源

前两篇我们把 Spark 的整体架构和 RDD 讲完了。这一篇转向 Spark SQL------也就是 Spark 里专门用来处理结构化数据的模块。你会搞明白 DataFrame 是什么、SQL 风格和 DSL 风格各自的写法、窗口函数和炸裂函数怎么用、以及 Spark 怎么和 MySQL、Hive 这类外部系统打交道。

一、Spark SQL 是干什么的?

Spark SQL 是 Spark 用于处理结构化数据的模块。它的核心价值在于:能把写好的 SQL 语句转换成 RDD 去执行,因此在同样一份 Hive 任务上,运行速度比 Hive 这类传统工具更快。

  • 处理对象是结构化数据------有明确字段和类型的数据,比如一张学生表、一份订单流水。
  • 运行环境是 SparkSession(这一点和 Spark Core 用 SparkContext 不同,后面会讲)。
  • 两种写法:SQL 风格和 DSL 风格,等价于"写 SQL"还是"写代码"两种口味。

二、两个核心抽象:DataFrame 与 DataSet

Spark SQL 的世界由两个核心抽象撑起来,理解它们就理解了 Spark SQL。

|---------|-----------------|----------------|
| 对比项 | DataFrame | DataSet |
| 定位 | Spark SQL 的基础抽象 | 更高级的抽象 |
| 形态 | 分布式、带分区的数据容器 | 与 DataFrame 类似 |
| 类比 | 像传统数据库里的二维表格 | 带类型约束的表格 |
| 特点 | 记录了数据的结构信息 | 类型更严格 |

可以这样记:DataFrame 像一张"带表头的二维表",一行一行整齐对齐,而且能自动记住每一列是什么类型;DataSet 则在此基础上把类型约束得更死一些。

需要澄清的一个常见误解:DataSet 这个概念源自 Scala。在 PySpark 里并没有提供强类型的 DataSet,而是直接用 DataFrame 来实现类似的功能。所以你在 PySpark 里看到的就是 DataFrame。

三、运行入口:SparkSession

在 Spark Core 里,我们用 SparkContext 来创建 RDD;而在 Spark SQL 里,对应的入口是 SparkSession。它可以理解为 Spark 应用的"总控台",既管理 DataFrame 的读取与操作,也兼容 SQL 查询。

from pyspark.sql import SparkSession

spark = (SparkSession.builder
.master("local\*")
.appName("spark-sql-demo")
.getOrCreate())

四、两种数据处理风格:SQL 与 DSL

同样是查数据,Spark SQL 提供了两条路径,殊途同归。

|---------|------------------------|----------------------------|
| 对比项 | SQL 风格 | DSL 风格 |
| 写法 | 用 spark.sql() 写 SQL 语句 | 用 DataFrame API 链式调用 |
| 常用方法 | sql()、createTempView() | select、filter、groupBy、join |
| 优点 | 贴近 SQL 生态,逻辑直观 | 类型提示友好,便于组合与复用 |
| 企业实践 | 适合已有 SQL 资产的场景 | 更倾向使用 DSL |

同样一句统计需求,两种写法

------ SQL 风格 ------

spark.sql("select dept, count(*) as cnt from staff group by dept").show()

------ DSL 风格 ------

df.select("dept").groupBy("dept").count().show()

五、SQL 风格下的三类基础操作

1. 字段计算

用 SELECT 语句对表里的字段做数学运算,并给算出来的新字段起个别名。

select name, age, age + 1 as age_add, age - 1 as age_sub
from student

2. 筛选数据

用 WHERE 子句按条件过滤,比如查年龄大于 20 的学生。

select name, age from student where age > 20

3. 分组统计

用 GROUP BY 分组,再配合 COUNT 统计每组的数量,这是最常见的一类统计需求。

select dept, count(*) as cnt from staff group by dept

六、DSL 风格三个实战场景

1. 读 CSV 并定义表结构

DSL 编程的第一步通常是读数据。用 read_csv 读取时可以用 schema 参数明确指定表结构,这样字段类型才是确定的,不会被默认推断带偏。

from pyspark.sql.types import StructType, StructField, StringType, IntegerType

schema = StructType( StructField("id", StringType(), True), StructField("name", StringType(), True), StructField("age", IntegerType(), True), StructField("country", StringType(), True), )
df = spark.read.csv("hdfs://node1/students.csv", header=True, schema=schema)

2. 窗口函数:row_number() 做排名

窗口函数最常见的用途是排名。row_number() 会给每行数据在它的分组范围内编号,配合 partition by(按什么分组)和 order by(按什么排序)两个子句,就能实现各种维度的排名,比如"按国家分别排名"。

from pyspark.sql.window import Window

w = Window.partitionBy("country").orderBy("age", desc=True)
df.withColumn("rank", row_number().over(w)).show()

3. 炸裂函数:explode 把一行拆成多行

有一类很常见的数据形态:几个值被塞在同一个字段里,比如一个字符串是 "apple banana"。如果想按单词统计次数,就要用 explode 把它"炸"开成多行,再做分组计数。

df.createOrReplaceTempView("fruit") # 建临时视图
spark.sql("""
select word, count(*) as cnt
from fruit
lateral view explode(split(words, ' ')) t as word
group by word
""").show()

这里也体现了两种风格的差异:同样的逻辑,DSL 写法大致是 explode 拆列 → groupBy 计数 → 聚合,SQL 写法则借助 lateral view explode 完成拆列。两种风格各有优劣,企业实践中更倾向于使用 DSL。

4. 表连接:join 与字段别名

两张表有共同键(比如 id)时,可以用 join 把它们连起来,常用的是内连接(inner join),只保留两边都能匹配上的行。

result = df_a.join(df_b, on="id", how="inner")

两张表有同名字段(name、id)时,用 select + alias 区分来源

result.select(
col("a.name").alias("name_a"),
col("b.name").alias("name_b"),
"id"
).show()

七、RDD 与 DataFrame 的相互转换

RDD 和 DataFrame 是同一份数据的两种视图,它们之间可以来回切换,是打通 Spark Core 与 Spark SQL 的桥梁。

|-----------------|------------------------------------------|---------|
| 转换方向 | 怎么做 | 要点 |
| RDD → DataFrame | 先把 RDD 转成键值对,再用 createDataFrame 传 Schema | 必须定义表结构 |
| DataFrame → RDD | 直接取 df.rdd 属性 | 非常简单 |

RDD → DataFrame:先转 KV 格式,再带 Schema 创建

kv_rdd = sc.textFile("hdfs://node1/students.txt").map(lambda line: line.split(",")) \
.map(lambda x: (x0, x1, int(x2)))

df = spark.createDataFrame(kv_rdd, schema)

DataFrame → RDD:取 rdd 属性即可

back_rdd = df.rdd

Schema 怎么构建

Schema 是把 RDD 变成结构化数据的关键一步。它使用 StructType 类来定义,里面每一列是一个 StructField,由若干个 StructField 组成的列表就是一个完整的 Schema。常用类型有 StringType(字符串)、IntegerType(整数)等。

八、自定义函数(UDF)

当内置函数满足不了复杂业务需求时,可以自己写函数。用法上有两个要点:

  • 定义方式很自由:可以写普通 Python 函数、用 lambda 表达式,或者写用户自定义函数(UDF)。
  • 关键一步:使用前必须通过 spark.udf.register() 注册,注册后才能在 SQL 查询里被识别和调用。
  • 注册时可以顺便指定一个别名,方便在 SQL 语句中直接引用。

1. 定义函数

def add_tag(age):
return "young" if age < 20 else "old"

2. 注册(并起别名),之后才能在 SQL 里用

spark.udf.register("add_tag", add_tag)

3. 在 SQL 中调用

spark.sql("select name, add_tag(age) as tag from student").show()

九、Spark SQL 的数据源

1. Parquet

Parquet 是一种高效的列式存储格式,压缩比高、查询性能好,是 Spark 生态里最常用的存储格式之一。

2. JDBC:读写关系型数据库

Spark SQL 支持通过 JDBC 协议读写 MySQL、PostgreSQL 这类关系型数据库。

读取:需要 JDBC URL、用户名、密码、驱动类名等参数

df = spark.read.jdbc(
url="jdbc:mysql://node1:3306/mydb",
table="student",
properties={"user": "root", "password": "******",
"driver": "com.mysql.cj.jdbc.Driver"}
)

写入:同样要配 URL、表名和写入模式

df.write.jdbc(
url="jdbc:mysql://node1:3306/mydb",
table="student_out",
mode="overwrite", # 可选 overwrite / append 等
properties={"user": "root", "password": "******",
"driver": "com.mysql.cj.jdbc.Driver"}
)

  • 写入模式(mode)决定写入行为,比如 overwrite 是覆盖目标表,append 是追加数据。
  • 如果目标表还不存在,Spark 写入时会自动创建一张结构对应的表。

十、Spark 与 Hive 的整合

Spark 和 Hive 整合有两种模式,方向不同、分工不同。

|---------------|--------------------|--------------------------|
| 模式 | 谁执行 | 说明 |
| Spark on Hive | Spark 引擎执行 HiveQL | 用 Spark 的速度跑 Hive 写的 SQL |
| Hive on Spark | Hive 引擎执行 Spark 任务 | 用 Hive 的调度管理 Spark 任务 |

整合的前提有三个:Hive 服务已经正常启动、Spark 侧配置好 Hive 元数据存储(metastore)的地址、并且把 MySQL 驱动 JAR 包放到 Spark 的 lib 目录下,让 Hive 能正确连上元数据库。

连上之后,就可以用 Spark SQL 在 Hive 里建表、插数据、查数据;同样的操作也可以用 Python 代码来实现,两种方式最终都是走 Spark 的执行引擎。

十一、常见故障的排查思路

最后说两个绕不开的"坑",了解一下原理就不容易慌。

1. 安全模式

Hadoop 的 NameNode 在系统冷启动后有时会进入"安全模式",此时服务看起来启不来、数据也无法正常读写。处理思路是先用专门命令把安全模式关掉,再重新启动服务即可。

2. 数据丢失

如果虚拟机直接关机导致进程被强行中断,可能引发 HDFS 上的数据丢失。处理思路是清理掉 HDFS 里相关的临时文件(比如 blk 临时文件)以及 Hive 的历史服务数据,然后重新启动服务,让集群恢复到干净状态。

十二、一句话记住 Spark SQL

Spark SQL 用 DataFrame 把分布式数据变成了"带表头的表格",用 SparkSession 作为入口,支持 SQL 风格和 DSL 风格两种写法;数据可以来自 CSV、JSON、Parquet,也可以通过 JDBC 连到 MySQL,甚至和 Hive 打通。工具链齐了,剩下的就是熟练度和业务理解了。

相关推荐
大鳥41 分钟前
JSON 解析最佳实践:从入门到生产级优化
hive·sql·spark·json
凤山老林44 分钟前
授权模型只会RBAC?深入 ABAC 授权模型,实现权限判断做成策略引擎
大数据·数据库·算法
starzy199044 分钟前
KafkaProducer 源码深度剖析:支撑高吞吐的数据结构设计
大数据·数据结构·kafka
饼饼学习空间智能1 小时前
机器人规模化部署平台怎么选?Fleet、VDA 5050、多机器人调度与运营闭环解析
大数据·深度学习·机器学习
geovindu1 小时前
sql:SQL Views and Materialized Views using mysql 9.0
大数据·数据库·数据仓库·sql·mysql·数据库开发·数据库架构
浔溺1 小时前
Spark核心基石:RDD全方位深度解析(原理、算子、实战、优化)
大数据·分布式·spark
梦影_1 小时前
MySQL进阶教程(二)—— 储存过程与触发器(下)
开发语言·数据库·后端·sql·mysql·oracle·数据库开发
Sayai1 小时前
Elasticsearch Rollup 汇算索引实战:监控指标多粒度预聚合查询,附 painless 时间窗取每日最新值
大数据·运维·elasticsearch·搜索引擎·性能优化·监控
ha_lydms1 小时前
Xml Functions(XML 函数)
xml·大数据·hadoop·spark·数据处理·dataworks·maxcompute