环境搭建与快速上手:Spark 与 Delta Lake 集成、本地开发环境配置、第一个 Delta 表
说实话,很多人在学 Delta Lake 时,第一步就被环境卡住了。配置半天跑不起来,热情直接凉半截。我个人习惯是先把环境搞利索,再动手写代码。今天我们就从零开始,把 Spark 和 Delta Lake 的集成环境搭好,然后跑通你的第一个 Delta 表。
为什么选这套技术栈?
Delta Lake 本身不是独立服务,它是个存储层。说白了,它寄生在 Spark 之上。你想想看,没有 Spark 的计算能力,Delta Lake 的事务和版本管理就无从谈起。所以我们的环境核心就是:Spark + Delta Lake 插件。
我在项目中遇到过不少团队,直接用 Spark 自带的 Parquet 格式做数仓,结果数据一致性问题频发。后来迁移到 Delta Lake,环境配置其实就多了几行依赖而已。
版本选型:别踩坑
版本匹配是最大的坑。我曾经因为 Spark 3.0 配了 Delta Lake 2.0,结果一堆 API 不兼容,排查了两天。下面这张表是我总结的稳定组合:
| Spark 版本 | Delta Lake 版本 | Scala 版本 | 状态 |
|---|---|---|---|
| 3.5.x | 3.2.x | 2.13 | 推荐 |
| 3.4.x | 3.1.x | 2.13 | 稳定 |
| 3.3.x | 2.4.x | 2.12 | 可用 |
| 3.2.x | 2.2.x | 2.12 | 旧版 |
本地开发环境配置
本地开发我推荐用 IntelliJ IDEA + Maven/Gradle。别用 Spark Shell 写生产代码,那玩意儿只适合临时调试。
1. Maven 依赖配置
在 pom.xml 里加入以下核心依赖:
<properties>
<scala.version>2.13.12</scala.version>
<spark.version>3.5.1</spark.version>
<delta.version>3.2.0</delta.version>
</properties>
<dependencies>
<!-- Spark Core -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.13</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- Spark SQL -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.13</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- Delta Lake -->
<dependency>
<groupId>io.delta</groupId>
<artifactId>delta-spark_2.13</artifactId>
<version>${delta.version}</version>
</dependency>
</dependencies>
build.gradle 里加上 implementation 'io.delta:delta-spark_2.13:3.2.0'。我习惯把版本号抽到 gradle.properties 里统一管理。
2. SparkSession 初始化
这是最关键的一步。很多人忘了加 Delta Lake 的扩展包,结果表创建失败。看我的标准写法:
import org.apache.spark.sql.SparkSession
val spark = SparkSession.builder()
.appName("DeltaLakeDemo")
.master("local[*]")
.config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension")
.config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog")
.getOrCreate()
// 验证是否加载成功
println(spark.version)
嗯,这里要注意 spark.sql.extensions 和 spark.sql.catalog 这两个配置。少了任何一个,Delta Lake 的语法糖都用不了。
第一个 Delta 表
环境搭好了,我们来写第一个 Delta 表。我会用两种方式:DataFrame API 和 SQL。你选顺手的方式就行。
方式一:DataFrame API
import spark.implicits._
// 创建示例数据
val data = Seq(
("Alice", 28, "Engineering"),
("Bob", 35, "Marketing"),
("Charlie", 42, "Sales")
).toDF("name", "age", "department")
// 写入 Delta 表
data.write
.format("delta")
.mode("overwrite")
.save("/tmp/delta/employee")
// 读取验证
val df = spark.read.format("delta").load("/tmp/delta/employee")
df.show()
方式二:SQL 方式
// 创建 Delta 表
spark.sql("""
CREATE TABLE IF NOT EXISTS employee_delta (
name STRING,
age INT,
department STRING
) USING DELTA
LOCATION '/tmp/delta/employee_sql'
""")
// 插入数据
spark.sql("""
INSERT INTO employee_delta VALUES
('Alice', 28, 'Engineering'),
('Bob', 35, 'Marketing'),
('Charlie', 42, 'Sales')
""")
// 查询
spark.sql("SELECT * FROM employee_delta").show()
验证你的 Delta 表
表创建成功后,你可以检查一下 Delta Lake 的独有特性。运行下面这段代码:
// 查看表历史版本
spark.sql("DESCRIBE HISTORY employee_delta").show(truncate = false)
// 查看表详情
spark.sql("DESCRIBE DETAIL employee_delta").show(truncate = false)
你会看到类似这样的输出:
| version | timestamp | operation | operationParameters |
|---|---|---|---|
| 1 | 2024-01-15 10:30:00 | INSERT | {"mode":"Append","partitionBy":"[]"} |
| 0 | 2024-01-15 10:29:00 | CREATE TABLE | {"isManaged":"false","description":null} |
看到没?Delta Lake 自动记录了每次操作的历史。这就是事务日志的威力。你想想看,如果是普通 Parquet 表,这些信息根本查不到。
核心知识体系
我把本章的知识结构画了张图,方便你理解整体脉络:
常见问题排查
环境搭建总会遇到各种幺蛾子。我列几个高频问题:
- ClassNotFoundException: DeltaSparkSessionExtension —— 多半是依赖没加全,或者版本不匹配。检查
pom.xml里的delta-spark包。 - AnalysisException: Delta table not found —— 路径写错了,或者表格式不是 DELTA。用
spark.read.format("delta").load(path)明确指定。 - SparkException: Failed to merge incompatible data types —— Schema 变更了。Delta Lake 默认会做 Schema 校验,你可以用
.option("mergeSchema", "true")允许合并。
master("local[*]") 的星号表示用所有可用核心。如果你的电脑只有 4 核,别贪心,改成 local[2] 就行。我曾经在 8 核机器上开 8 个线程跑本地测试,结果 IDE 直接卡死。
好了,环境搭好,第一个表也跑通了。你现在可以试试往表里追加几条数据,然后查一下历史版本。Delta Lake 的魅力,从这一刻开始展现。