环境搭建:Spark 与 Delta Lake 版本兼容性、Maven/SBT 依赖配置、本地开发环境(Docker + Jupyter)搭建
说实话,做实时特征工程,环境搭不好,后面全是坑。我见过太多团队,代码写得很漂亮,结果一跑就报错——版本不兼容、依赖冲突、本地跟线上环境不一致。嗯,这节课我们就一次性把这些事理清楚。
核心要点:Delta Lake 对 Spark 版本有严格依赖,选错版本会让你多花一整天排错。我个人习惯是先确定 Spark 版本,再选 Delta Lake,最后配 Maven/SBT。
版本兼容性:选对组合,少走弯路
Delta Lake 本质上是一个 Spark 的扩展库,它通过修改 Spark 的查询计划来实现 ACID 和版本控制。所以,版本匹配是第一步。
我整理了一张兼容性表,这是我在多个项目中验证过的:
| Delta Lake 版本 | Apache Spark 版本 | Scala 版本 | 备注 |
|---|---|---|---|
| 2.4.x | 3.4.x | 2.13 | 推荐生产使用,稳定 |
| 3.0.x | 3.5.x | 2.13 | 最新特性,但部分 API 有变动 |
| 2.3.x | 3.3.x | 2.12 / 2.13 | 旧项目常见,注意 Scala 版本 |
| 1.2.x | 3.2.x | 2.12 | 已停止维护,不建议新项目使用 |
我曾经踩过的坑:有一次我用了 Delta Lake 2.4.0 搭配 Spark 3.3.2,结果运行 MERGE 操作时一直报 UnsupportedOperationException。查了半天才发现,Delta 2.4 要求 Spark 3.4 以上。所以,一定要严格按照官方兼容性表来,别想当然。
为什么会这样?因为 Delta Lake 的底层实现依赖 Spark 内部的 FileFormatWriter 和 TransactionLog 接口,这些接口在不同 Spark 版本间有细微差异。Delta 团队在每个版本里都会针对特定 Spark 版本做适配。
Maven / SBT 依赖配置:别让依赖冲突搞崩你
确定了版本组合,接下来就是配依赖。这里我给出两种构建工具的配置模板,你直接复制改版本号就行。
Maven 配置(pom.xml)
<properties>
<spark.version>3.4.3</spark.version>
<delta.version>2.4.0</delta.version>
<scala.binary.version>2.13</scala.binary.version>
</properties>
<dependencies>
<!-- Spark Core -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
<scope>provided</scope>
</dependency>
<!-- Spark SQL -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
<scope>provided</scope>
</dependency>
<!-- Delta Lake -->
<dependency>
<groupId>io.delta</groupId>
<artifactId>delta-core_${scala.binary.version}</artifactId>
<version>${delta.version}</version>
</dependency>
</dependencies>
小技巧:Spark 依赖用 provided scope,因为运行时 Spark 集群已经提供了这些 jar。Delta Lake 用默认 scope,它会被打包进你的 fat jar 里。这样能避免集群自带的 Spark 和你的版本冲突。
SBT 配置(build.sbt)
name := "delta-feature-pipeline"
version := "1.0"
scalaVersion := "2.13.10"
val sparkVersion = "3.4.3"
val deltaVersion = "2.4.0"
libraryDependencies ++= Seq(
"org.apache.spark" %% "spark-core" % sparkVersion % "provided",
"org.apache.spark" %% "spark-sql" % sparkVersion % "provided",
"io.delta" %% "delta-core" % deltaVersion
)
你看,其实就三行核心依赖。但要注意,如果你要用 Delta Lake 的 Streaming 功能,还得额外加一个:
// 如果需要 Delta Streaming Sink/Source
"io.delta" %% "delta-storage" % deltaVersion
本地开发环境:Docker + Jupyter 一键搭建
我个人强烈建议用 Docker 搭本地环境。为什么?因为你在 Mac 上装 Spark,光是解决 native 库和 Hadoop 的兼容问题就能折腾半天。用 Docker,一个镜像全搞定。
下面是我自己用的 Docker Compose 配置,你直接保存成 docker-compose.yml:
version: '3.8'
services:
spark-delta:
image: jupyter/all-spark-notebook:spark-3.4.3
ports:
- "8888:8888" # Jupyter Notebook
- "4040:4040" # Spark UI
volumes:
- ./workspace:/home/jovyan/work
environment:
- JUPYTER_ENABLE_LAB=yes
- SPARK_OPTS=--packages io.delta:delta-core_2.13:2.4.0
command: start-notebook.sh --NotebookApp.=''
启动命令很简单:
docker-compose up -d
等几秒钟,打开 http://localhost:8888,就能看到 Jupyter Lab 界面了。
注意:上面的 SPARK_OPTS 环境变量是关键。它告诉 Spark 启动时自动加载 Delta Lake 的 jar 包。如果你忘了配,在 Notebook 里 import io.delta.tables._ 会直接报 ClassNotFoundException。
验证环境是否正常
在 Jupyter 里新建一个 Notebook,选 Python 3 内核,跑下面这段代码:
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.appName("DeltaCheck") \
.config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") \
.config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog") \
.getOrCreate()
# 创建一个 Delta 表试试
data = [("Alice", 34), ("Bob", 45), ("Cathy", 29)]
df = spark.createDataFrame(data, ["name", "age"])
df.write.format("delta").save("/tmp/delta_test")
# 读回来
df_read = spark.read.format("delta").load("/tmp/delta_test")
df_read.show()
如果能看到三行数据输出,说明环境搭好了。嗯,就是这么简单。
知识体系总览
我把这一章的核心逻辑画了张图,方便你理解各个组件之间的关系:
避坑指南:我踩过的几个雷
- Spark 内置 Delta 版本冲突:有些 Spark 发行版(比如 Databricks Runtime)自带了 Delta Lake。如果你在本地用不同的版本,打包时记得用
shade插件重命名包路径,或者用--conf spark.sql.extensions指定你自己的版本。 - Hadoop 本地库缺失:在 Windows 上跑 Spark,经常报
Failed to locate the winutils binary。解决办法是下载winutils.exe放到%HADOOP_HOME%\bin下。用 Docker 的话就没这个问题。 - Jupyter 内核内存不足:默认的 Docker 镜像只给了 1GB 内存。如果你要处理稍大的数据,记得在
docker-compose.yml里加mem_limit: 4g。
我的习惯:每次搭好环境,我都会先跑一遍 Delta Lake 官方的 Quickstart 示例。不是为了学 API,而是为了确认整个链路——从 SparkSession 初始化到 Transaction Log 写入——都是通的。这一步花不了 5 分钟,但能省下后面排错的几小时。
好了,环境搭好,我们就可以开始写真正的特征工程代码了。记住,版本选对、依赖配好、环境一致,后面的事情就顺了。