3. 环境搭建:Spark + Delta Lake 集成环境配置(本地与云环境)

说实话,很多同学学 Delta Lake 卡住,不是因为概念难懂,而是环境没搭好。我见过太多人花了两天时间配环境,最后跑个 spark-shell 都报错。嗯,这一章我们就来把这个事彻底搞定。

我会带你走两条路:本地开发环境和云环境。本地环境适合学习和调试,云环境适合生产。两条路我都会给出我踩过坑之后的「最优解」。

核心原则:Delta Lake 本质上是一个 Spark 的扩展库(delta-core),不是独立服务。所以你的核心任务是:让 Spark 能正确加载 Delta Lake 的 jar 包。

Spark + Delta Lake 集成环境架构图 Spark + Delta Lake 集成环境全景 本地开发环境 Spark 3.x Delta Lake 2.x Hadoop 3.x 本地文件系统 云环境(AWS / Azure / GCP) EMR / Databricks Delta Lake 2.x S3 / ADLS / GCS Hive Metastore 关键依赖版本对应关系 Spark 3.3.x → Delta Lake 2.3.x | Spark 3.4.x → Delta Lake 2.4.x Spark 3.5.x → Delta Lake 3.0.x | 务必使用 Scala 2.12/2.13 对应版本

3.1 版本匹配:最容易翻车的地方

先聊版本。Delta Lake 和 Spark 的版本绑定非常紧。用错了版本,启动就报 NoSuchMethodError,你查半天都不知道问题在哪。我当年在第一个生产项目里就吃过这个亏——Delta 0.6 配 Spark 2.4,结果事务日志写不进去,排查了两天。

下面这张表是我整理的最新稳定版本对应关系,你直接照着选就行:

Spark 版本 Delta Lake 版本 Scala 版本 备注
3.3.x 2.3.x 2.12 / 2.13 最稳定组合,推荐学习使用
3.4.x 2.4.x 2.12 / 2.13 支持 Liquid Clustering
3.5.x 3.0.x 2.12 / 2.13 最新版,支持 Deletion Vectors

我的建议:学习阶段用 Spark 3.3.2 + Delta Lake 2.3.0。这个组合文档最全,社区最活跃,遇到问题 Stack Overflow 上基本都有答案。

3.2 本地环境搭建(Mac / Linux)

本地环境我习惯用 Homebrew + 手动配置的方式。为什么不用 Docker?因为本地调试时,你经常需要改配置、看日志,Docker 的隔离反而碍事。

3.2.1 安装 Spark

先确认你装了 Java 8 或 11:

java -version
# 输出示例:openjdk version "11.0.18" 2023-01-17 LTS

然后下载 Spark(不带 Hadoop 的预编译版):

# 下载 Spark 3.3.2
wget https://archive.apache.org/dist/spark/spark-3.3.2/spark-3.3.2-bin-without-hadoop.tgz

# 解压到 /opt 目录
sudo tar -xzf spark-3.3.2-bin-without-hadoop.tgz -C /opt/
sudo ln -s /opt/spark-3.3.2-bin-without-hadoop /opt/spark

# 配置环境变量(加到 ~/.zshrc 或 ~/.bashrc)
export SPARK_HOME=/opt/spark
export PATH=$SPARK_HOME/bin:$PATH
export SPARK_DIST_CLASSPATH=$(hadoop classpath)

这里有个坑:SPARK_DIST_CLASSPATH 如果不设置,Spark 启动时会找不到 Hadoop 的依赖。我第一次配的时候就漏了这步,结果 spark-shell 一直报 ClassNotFoundException。

3.2.2 集成 Delta Lake

Delta Lake 的集成方式有两种:启动时指定 jar 和 编译进 Spark。我推荐第一种,灵活,换版本方便。

# 下载 Delta Lake 2.3.0 的 jar
wget https://repo1.maven.org/maven2/io/delta/delta-core_2.12/2.3.0/delta-core_2.12-2.3.0.jar

# 放到 Spark 的 jars 目录
cp delta-core_2.12-2.3.0.jar $SPARK_HOME/jars/

验证是否成功:

spark-sql --packages io.delta:delta-core_2.12:2.3.0 \
  --conf "spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension" \
  --conf "spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog"

# 进入 spark-sql 后执行
SHOW DATABASES;
# 如果能正常显示,说明集成成功

注意:如果你用 spark-submit 提交作业,记得在 --conf 里加上上面两个配置。很多人只在 spark-shell 里配了,提交作业时发现 Delta Lake 的表读不了——嗯,我当年也犯过这个错。

3.2.3 配置本地文件系统作为 Delta Lake 存储

本地开发时,我建议用 file:// 协议直接写本地磁盘。这样调试最方便,不用依赖任何外部服务。

# 创建测试目录
mkdir -p /tmp/delta-demo

# 启动 spark-shell 并测试
spark-shell \
  --conf "spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension" \
  --conf "spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog"

# 在 Scala shell 中执行
val data = spark.range(0, 100)
data.write.format("delta").save("file:///tmp/delta-demo/test-table")
spark.read.format("delta").load("file:///tmp/delta-demo/test-table").show()

看到 100 条数据输出,恭喜你,本地环境搭好了。

3.3 云环境搭建(以 AWS EMR 为例)

云环境我以 AWS EMR 为例。为什么选 EMR?因为它是目前国内用得最多的 Spark 托管服务。Azure HDInsight 和 GCP Dataproc 的配置思路类似,我就不重复了。

3.3.1 创建 EMR 集群

创建 EMR 集群时,有几个关键配置:

  • EMR 版本:选 6.9.0 及以上(内置 Spark 3.3.x)
  • 应用:勾选 Spark、Hive、Hadoop
  • 硬件:学习用 1 个 Master + 2 个 Core(m5.xlarge 就够)
  • 安全组:开放 22 端口(SSH)和 8890(Spark History Server)

集群启动后,Delta Lake 默认没有安装。你需要通过 引导操作(Bootstrap Action) 来安装。

3.3.2 引导操作安装 Delta Lake

在 EMR 控制台创建集群时,添加引导操作:

#!/bin/bash
# delta-lake-bootstrap.sh

# 下载 Delta Lake jar 到 Spark 的 classpath
DELTA_VERSION="2.3.0"
SPARK_JARS_DIR="/usr/lib/spark/jars"

wget -P $SPARK_JARS_DIR \
  https://repo1.maven.org/maven2/io/delta/delta-core_2.12/$DELTA_VERSION/delta-core_2.12-$DELTA_VERSION.jar

# 配置 Spark 默认参数
cat >> /etc/spark/conf/spark-defaults.conf << EOF
spark.sql.extensions io.delta.sql.DeltaSparkSessionExtension
spark.sql.catalog.spark_catalog org.apache.spark.sql.delta.catalog.DeltaCatalog
EOF

经验之谈:引导操作脚本一定要加 set -e,否则脚本中间失败了你都不知道。我曾经因为网络问题没下载到 jar,集群启动了但 Delta Lake 不可用,排查了半天才发现是引导操作静默失败了。

3.3.3 配置 S3 作为 Delta Lake 存储

云环境的核心区别在于存储层。本地用 file://,云上要用对象存储。以 S3 为例:

# 创建 S3 桶(如果还没有)
aws s3 mb s3://delta-lake-demo-2024

# 启动 pyspark 并测试
pyspark \
  --conf "spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension" \
  --conf "spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog" \
  --conf "spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem" \
  --conf "spark.hadoop.fs.s3a.access.key=YOUR_ACCESS_KEY" \
  --conf "spark.hadoop.fs.s3a.secret.key=YOUR_SECRET_KEY"

# 在 Python shell 中执行
df = spark.range(0, 100)
df.write.format("delta").save("s3a://delta-lake-demo-2024/test-table")
spark.read.format("delta").load("s3a://delta-lake-demo-2024/test-table").show()

安全提醒:生产环境不要把 Access Key 写在命令行里。用 IAM 角色或者 AWS Secrets Manager。我见过有人把 Key 提交到 GitHub,结果一夜之间被刷了 2 万美元的账单——别问我怎么知道的。

3.4 环境验证清单

环境搭好后,我建议你跑一遍这个验证清单,确保所有组件都正常工作:

检查项 验证方法 预期结果
Spark 版本 spark-shell --version 显示 3.3.x 或以上
Delta Lake 加载 spark.sql("SELECT version()").show() 显示 Delta Lake 版本号
本地读写 写入 file:// 路径并读取 数据一致,无报错
S3 读写 写入 s3a:// 路径并读取 数据一致,无报错
事务日志 查看 _delta_log 目录 存在 00000000000000000000.json 文件

3.5 常见问题与避坑

最后,分享几个我实际项目中遇到的高频问题:

  • 问题:启动时报 NoClassDefFoundError: io/delta/sql/DeltaSparkSessionExtension
    原因:jar 没放到 classpath。检查 $SPARK_HOME/jars/ 下有没有 delta-core 的 jar。
  • 问题:写入 S3 时报 Access Denied
    原因:IAM 权限不够。确保角色有 s3:PutObject、s3:GetObject、s3:ListBucket 权限。
  • 问题:Delta Lake 表查询特别慢
    原因:本地环境默认没有做任何优化。可以加 --conf "spark.sql.adaptive.enabled=true" 开启 AQE。

一句话总结:环境搭建的核心就三件事——版本匹配、jar 包加载、存储配置。这三件事做对了,Delta Lake 就跑起来了。剩下的都是细节。