
在 IntelliJ IDEA 里跑 Spark 程序光是一个环境配置就能劝退一半新手。我见过太多人在第一步就卡死JDK 版本不对、Scala 版本跟 Spark 对不上、Maven 依赖下载慢得像蜗牛、好不容易跑起来又报一串 ClassNotFoundException。这篇文章我就把自己的配置流程完整过一遍从零开始到能本地跑通 WordCount再顺手解决几个高频报错。整套环境我平时拿来写 Spark SQL 和 Structured Streaming 都在用测试下来很稳。适合刚接触 Spark 想用 IDEA 做本地开发的读者也适合那些被环境折腾到怀疑人生、想直接抄一份可用配置的人。1. 环境选型与版本匹配先别急着下载安装版本匹配是整套环境的基石。Spark 是 Scala 写的底层跑在 JVM 上所以 JDK 和 Scala 的版本直接决定你能用哪个版本的 Spark。我实测过的稳定组合是 JDK 8 Scala 2.12 Spark 3.x。Spark 3.2 之后官方开始在 JDK 8 和 JDK 11 之间做兼容3.4 和 3.5 已经能跑在 JDK 17 上但你要是图省心JDK 8 是最不容易出问题的。社区里大量老项目、网上的教程、甚至企业内部模板默认都是 JDK 8。Scala 那边更要注意。Spark 每个大版本会发布针对不同 Scala 小版本的编译产物比如 spark-core_2.12 和 spark-core_2.13 是两个不同的坐标。你要是用 Scala 2.13 的编译器去调 Spark 2.12 编译出来的库运行时会直接报NoClassDefFoundError或者各种奇奇怪怪的方法签名错误。所以选 Scala 2.12配合 spark_2.12 系列的依赖是兼容性最优解。IDEA 方面建议用 2023.x 或 2024.x 的较新版本。太老的版本对 Scala 插件的兼容有问题2024.1 之后官方把 Scala 插件的维护方式改过你直接在插件市场搜 Scala 安装就行。社区版也能用Scala 插件和 Maven 支持在社区版里都是完整的没必要为了写 Spark 买 Ultimate。Maven 建议用 3.8 或 3.9IDEA 自带的 Maven 版本也可以但最好自己装一个并改好国内镜像不然下载依赖能等到怀疑人生。这个下面专门说。Linux 和 Windows 在配置上的区别主要在 Hadoop 的 winutils 和 native 库上Windows 直接跑 Spark local 模式会报缺少winutils.exe这是正常现象我有专门的处理方法后面的故障排查章节会详细写。2. 基础组件安装与镜像加速2.1 JDK 安装要点JDK 安装本身不复杂但有几个点容易坑到新手。第一是环境变量必须配JAVA_HOME和PATH这个不用说太多。第二是注意 32 位和 64 位的问题现在基本都装 64 位。第三是如果你机器上已经装过多个 JDKIDEA 里要手动指定 Project SDK 为你想要的版本否则编译阶段会出现 source/target 版本不匹配的报错。装完 JDK 在命令行跑一下java -version确认输出的是 1.8 而不是 openjdk 17 之类的版本。要是装了新版 JDKIDEA 编译时默认用最新 SDK而 Spark 3.2 之前的版本在 JDK 11 上跑会报IllegalAccessError这类问题排查起来很耗费时间。2.2 Scala 插件的安装与踩坑IDEA 装 Scala 插件有两类做法一是在插件市场直接搜 “Scala” 安装这种方式最省事二是在Settings - Languages Frameworks - Scala里设置编译器。我建议用第一种因为新版 IDEA 对 Scala 插件的管理已经整合进插件市场不需要额外下载离线包。装完插件后新建项目IDEA 会让你选 Scala SDK。如果你本机没有装 Scala可以直接在弹窗里选择 Download但 IDEA 默认下载源可能慢我都是先手动下载好 Scala 2.12.x 的压缩包解压后在 IDEA 里指定路径这样更可控。指定的时候注意选到 scala 目录本身也就是 bin 目录的上一级。2.3 Maven 镜像与本地仓库Maven 的配置是整个流程提速的关键。先在 Maven 的 conf/settings.xml 里配置阿里云镜像这一步比任何安装优化都管用。镜像配置放在mirrors节点里把官方中央仓库的访问全部拦截到阿里云镜像上。配置好镜像后再确认本地仓库路径。默认在~/.m2/repository如果你 C 盘空间紧张建议改到其他盘符。改完路径后IDEA 的 Maven 配置里也要同步修改 user settings file 的路径不然 IDEA 还是用默认配置。我自己的习惯是修改 IDEA 的Build Tools - Maven设置把 Maven home path 指向自装的 Maven把 User settings file 指向那份改好的 settings.xml然后勾选 Always update snapshots。这样后续集成 Scala 编译插件时快照版本的插件才能及时拉取更新。2.4 验证各组件环境全部装完后在 IDEA 的 Terminal 或者系统命令行里依次执行java -version mvn -version scala -version三个命令都正常输出版本号后基础环境就算就绪。有个细节要留意IDEA 内嵌的 Terminal 默认不会加载系统的环境变量配置如果你改了JAVA_HOME或PATH需要重启 IDEA 再开 Terminal不然跑的可能是旧路径。3. 五分钟创建一个可运行的 Spark 工程3.1 项目创建与骨架结构在 IDEA 里选 New Project左侧选 Maven勾选 Create from archetype但不要选默认的 maven-archetype-quickstart。我建议选一个带 Scala 支持的原型比如net.alchim31.maven:scala-archetype-simple如果拉取不了就选普通 Maven 项目后面手动加 Scala 目录结构。无论选什么方式最后手动创建的目录结构必须是这样src main java resources scala test java scalaIDEA 默认只创建 java 目录scala 目录要自己右键 New - Directory 创建。创建完后在 scala 目录上右键选 Mark Directory as - Sources Root。这一步不做IDEA 不会识别你的 Scala 源码目录写了代码也不给你编译。3.2 pom.xml 完整配置核心依赖就两个spark-core 和 spark-sql。网上很多教程会把 spark-hive、spark-streaming 一起加进去初期完全没必要加了只会拖慢依赖下载。我平时开发用的 pom 是这样的properties scala.version2.12.17/scala.version spark.version3.4.1/spark.version /properties dependencies dependency groupIdorg.scala-lang/groupId artifactIdscala-library/artifactId version${scala.version}/version /dependency dependency groupIdorg.apache.spark/groupId artifactIdspark-core_2.12/artifactId version${spark.version}/version /dependency dependency groupIdorg.apache.spark/groupId artifactIdspark-sql_2.12/artifactId version${spark.version}/version /dependency /dependencies注意 spark-core 和 spark-sql 的 artifactId 里带了_2.12这是 Scala 版本后缀。你本地装什么 Scala这里就写什么后缀两者不匹配运行阶段会炸。scope 先不加 provided本地跑需要完整的依赖包体等要打包提交集群时再改成 provided。provided 的含义是编译期和测试期依赖存在但运行时不打包由运行环境提供。本地跑 IDEA 会用 classpath 里的完整 jar两者不冲突。另外加一个编译插件不然后期写 Scala 代码时 IDEA 会提示找不到主类或者编译时根本不把 Scala 代码编进去。我用的插件配置如下build plugins plugin groupIdnet.alchim31.maven/groupId artifactIdscala-maven-plugin/artifactId version4.8.1/version executions execution goals goalcompile/goal goaltestCompile/goal /goals /execution /executions /plugin /plugins /build这个插件会把src/main/scala下的.scala文件编成 class 文件并和 Java 编译联动。没加这个插件你写 Scala 写得很开心但一运行就报ClassNotFound因为源码根本没被编译。3.3 第一个 WordCount 程序依赖配置好之后写一个简单的 WordCount 验证整个链路。在src/main/scala下建一个WordCount.scalaimport org.apache.spark.sql.SparkSession object WordCount { def main(args: Array[String]): Unit { val spark SparkSession.builder() .appName(WordCount) .master(local[2]) .getOrCreate() val rdd spark.sparkContext.textFile(src/main/resources/input.txt) val counts rdd.flatMap(_.split( )) .map(word (word, 1)) .reduceByKey(_ _) counts.collect().foreach(println) spark.stop() } }在src/main/resources下放一个input.txt随便写几行英文。运行主类控制台应该能看到每个单词的计数。这个方法跑通说明 JDK、Scala、Maven、Spark 依赖、IDEA 编译链路全部正常。这里我要特别说下master(local[2])这个参数的逻辑。local表示在本地模式下运行[2]表示使用两个线程模拟并发。你电脑是几核的就填几。如果你不写 master项目在集群环境跑时会从提交命令里获取 driver 的部署模式在本地调试时显式指定local[*]更直观。*代表用尽可用核数。开发调试阶段推荐这种方式简单直接。4. IDEA 里的调试技巧与提效配置4.1 日志配置别被 INFO 刷屏默认情况下 Spark 运行时会打印大量 INFO 日志跑一个小任务控制台几千行日志真正的结果被淹没。开发阶段我建议把日志级别调成 WARN。做法是创建log4j2.properties放到src/main/resources下Spark 3.x 使用 log4j2Spark 2.x 用 log4j.properties内容很简单rootLogger.level warn rootLogger.appenderRef.stdout.ref console appender.console.type Console appender.console.name console appender.console.layout.type PatternLayout appender.console.layout.pattern %d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n加了这份配置后运行时的输出会清爽很多只在报错才打印堆栈排查问题效率明显提升。如果你用 Spark 2.x对应的配置文件是log4j.propertieslog4j.rootCategoryWARN, console log4j.appender.consoleorg.apache.log4j.ConsoleAppender log4j.appender.console.targetSystem.err log4j.appender.console.layoutorg.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n4.2 断点调试与本地执行模式IDEA 调试 Scala 代码跟调试 Java 一样直接在行号旁边打红点然后右键 Debug 运行。但要注意 Spark 的 lazinessRDD 的所有转换操作都是懒执行的action 操作才真正触发作业计算。所以你要在collect()、count()、show()这些 action 处打断点不然前面一堆 flatMap、map、reduceByKey 的断点根本不会命中。另一个常见问题是在驱动端把大数据集collect()回来比如对一个几十 GB 的 RDD 执行.collect().foreach(println)driver 内存直接溢出。本地调试阶段数据量不大还好但我建议从一开始就养成好习惯调试用take(10)别用collect()。4.3 Spark 配置参数在 IDEA 里怎么传平时在集群上跑任务我们用spark-submit --executor-memory 2g --num-executors 4传资源参数。在 IDEA 本地开发时这些参数可以通过代码里的 SparkSession builder 去设置跟生产环境保持一致。我常用的本地模板是这样的val spark SparkSession.builder() .appName(LocalDebug) .master(local[*]) .config(spark.sql.shuffle.partitions, 4) .config(spark.driver.memory, 2g) .config(spark.memory.fraction, 0.6) .getOrCreate()spark.sql.shuffle.partitions默认是 200这个值在集群上可能合理但本地跑小数据时会导致生成大量小任务调度开销大于计算本身。调成 4 或 8本地运行速度能快不少。spark.memory.fraction是执行和存储共享堆内存的比例默认 0.6这是 Spark 内存模型里比较核心的参数executor 的 JVM 堆内存只有一部分能被 Spark 自由管理剩余部分留给用户代码和系统开销。5. 打包提交到集群时踩过的坑5.1 打 jar 包与 provided 的作用本地调试一切正常后你想把任务提交到集群这时才需要涉及打包。直接用 IDEA 自带的 Artifacts 打 jar 有时候会包含重复的依赖或者漏掉 Scala 类我建议用 Maven 的 package 生命周期。在打包之前把 pom 里的 spark-core 和 spark-sql 的 scope 改成 provided。这样打包出来的 jar 体积会小很多只包含你的业务代码和第三方非 Spark 依赖Spark 自身的 jar 在集群的共享目录里已经有了不需要打进 jar 包否则容易导致和集群版本冲突。我在生产环境就遇到过一次本地用的 Spark 3.4集群是 3.1.2打包时没改 scope提交后报各种NoSuchMethodError最后发现是 Spark 版本冲突换回 provided 后正常。5.2 集群提交流程打包好后把 jar 上传到集群节点执行提交命令spark-submit \ --class WordCount \ --master yarn \ --deploy-mode cluster \ --executor-memory 2g \ --num-executors 4 \ --executor-cores 2 \ wc.jar这个命令里--class必须是你的 object 完整路径--deploy-mode cluster表示 driver 运行在集群端。如果你没写--executor-cores有些集群客户端会默认每个 executor 只分配 1 个 vcore加上 Spark 默认调度策略基于容器分配经常出现你在 YARN 上看资源明明很充足、但任务就是只用很少 CPU 的情况。这正好说明了为什么--executor-cores必须显式设置包括spark.executor.cores也是同理。5.3 集群模式和本地模式的差异本地模式下所有事情都在一个 JVM 里完成driver 和 executor 不区分。集群模式则不同driver 负责调度任务executor 在多个节点上执行具体计算网络和数据访问的模式完全不同。在 IDEA 里跑通不代表在集群上一定能跑通两者最大的差异就在于是不是有真实的 HDFS 文件系统和 YARN 资源管理器。我开发代码的时候有个固定流程先在本地用 local 模式配小数据集把业务逻辑写完然后用spark-submit提交到测试集群验证最后再上生产。这样能大幅减少在集群上调试的尴尬。6. 高频报错与排查锦囊6.1 Bin file loaded in wrong path / Cannot run program winutils.exeWindows 用户最熟悉的问题。Spark 在 Windows 上找 Hadoop 的 native 依赖找不到就报这个错。解决方法是下载对应版本的 winutils.exe放到C:\hadoop\bin目录然后在代码里或者环境变量中指定System.setProperty(hadoop.home.dir, C:\\hadoop)或者在 IDEA 的运行配置的 VM options 里加-Dhadoop.home.dirC:\\hadoop。还有一种做法是把 winutils 所在目录加到系统 PATH 里但我在实际使用中发现显式设置hadoop.home.dir最稳对全局无副作用。6.2 Java.lang.NoClassDefFoundError: scala/Any这个报错基本都是 Scala 版本不匹配导致的运行时找到的 scala-library jar 版本和编译时不一致。检查两个地方一是 pom 里 Scala 版本跟本地安装的版本是否统一二是 IDEA 项目结构中 Scala SDK 是否设置正确。我遇到过一种隐蔽情况IDEA 自动解析依赖时把 scala-library 从 2.12.17 降到了 2.12.8因为另一个依赖传递引入的旧版本覆盖了显式声明。这种情况可以在 pom 里对 scala-library 显式声明 versionMaven 会优先使用直接依赖声明的版本。6.3 Spark User class not found: XXX这种报错大多是 Scala 类编译产物没有打进 jar 包。检查一下是否漏了 scala-maven-plugin 或者没有执行 compile 阶段。另外确认在 IDEA 里 object 的完整类名要写对比如你的 package 是com.example.spark那--class就写com.example.spark.WordCount。6.4 Executor 启动失败 / 分配到的资源不够出现这类现象要同时看 YARN 资源池和 Spark 参数。热词里有人问 “spark on yarn cpu 只能用 1 个是为什么”这是个非常典型的问题。原因通常有两个要么你没有设置spark.executor.cores和--executor-cores调度器按默认值 1 分配要么是 YARN 队列配置对每个 container 的最高 vcore 数有限制你申请 4 个队列最多只能给 2 个。解决方式是先用yarn node -list和yarn application -status appId查资源详情确认瓶颈在哪个层面再调整参数。6.5 内存溢出 / Container killed on memoryContainer killed by YARN for exceeding memory limits这个报错我见过的次数最多。核心原因是 executor 的 JVM 堆内存加上堆积外内存overhead超过了 YARN container 允许的上限。解决方式有两个方向一是调大spark.yarn.executor.memoryOverhead一般默认是 executor 内存的 10%C 程序或复杂 UDF 可能需要调到 20% 到 30%二是检查代码里有没有把大量数据攒在 driver 端。除此之外spark.memory.offHeap.enabled和spark.memory.offHeap.size可以配置堆外内存但配置不当反而更容易触发 YARN 的内存上限。开发阶段更推荐先控制数据规模。6.6 日志报错速查表我把平时遇到频率最高的错误和解决方向整理成一个速查表配置环境时可以直接对着查报错信息直接原因常规处理Connection refused: connect本地连不上集群的 8080/7077 端口检查网络或改用 local[2] 调试Failed to locate the winutils binaryWindows 缺少 Hadoop native下载 winutils 并设置 hadoop.home.dirContainer killed on memoryexecutor 总内存超过 YARN 限制调大 overhead 或降低执行内存Java.lang.OutOfMemoryError: Java heap spacedriver 或 executor 堆内存不足调大 spark.driver.memory 或减少数据量NoSuchMethodErrorSpark 依赖版本与集群不一致打包时把 Spark 依赖设为 providedjava.lang.ClassNotFoundException: WordCount主类没有编进 jar检查 scala-maven-plugin 和编译产物6.7 Spark SQL 开发时容易忽略的问题如果你用 Spark SQL 做开发还有几个点值得注意。一个是spark.sql.adaptive.enabled从 Spark 3.0 开始 AQE自适应查询执行是个开关控制的特性本地开发时数据量小、不明显但到集群跑大表时差别巨大。建议在代码里显式开启.config(spark.sql.adaptive.enabled, true) .config(spark.sql.adaptive.coalescePartitions.enabled, true)另一个常见坑是 UDF 用的第三方库没打进去提交集群运行时报ClassNotFound。解决办法是打 fat jar把第三方依赖一并打进去或者用--jars参数把依赖 jar 带上去本地跑。有些人以为和 Spark 依赖一样加 provided 就行结果 UDF 运行时找不到依赖类。这块的逻辑不同Spark 自己的 jar 集群里有第三方库集群里可没有不能一概而论。6.8 新环境初始化检查清单如果换了一台新电脑或者换了 IDE 版本我建议按下面这个清单逐项检查能把配置时间控制在 20 分钟以内JDK 版本确认命令行执行java -version确认是 1.8 或 11重点看项目编译用的 SDK 和运行用的 JRE 要一致。Maven 的 settings.xml 镜像地址确认检查本地仓库路径是否存在且有写入权限。Scala 插件已安装且在Project Structure - Global Libraries里能看到。IDEA 的 Maven Runner 设置里JRE 选择正确避免用了 IDEA 默认的较新 JDK。新建工程后确认src/main/scala已经被标记为 Source Root。运行前检查 SparkSession 里有没有master(local[*])如果保留了集群模式地址本地会一直连接不成功。首次运行前先执行mvn clean compile确认编译链路是通的再点 Debug。我个人在实际操作中的体会是Spark 开发环境配置没什么高深技术核心就是三件事版本统一切勿混搭、镜像配好节省时间、先本地后集群的调试顺序。版本这块最反直觉你选的 Scala 版本跟 Spark 编译时的版本差一个小版本坏的代码看起来却像你自己的逻辑问题排查起来很崩溃。所以我一般写完 pom 就顺手跑一次mvn dependency:tree把 Scala 库相关依赖的版本看一遍确认没有冲突再开始写代码。最后再分享一个小技巧IDEA 里跑 Spark 程序第一次类加载比较慢这是正常的因为 Spark 要初始化很多东西。如果你反复改代码调试建议保留一个 SparkSession 复用的方法别每个 main 函数都重新getOrCreate哪怕是在不同类里跑同一份配置也可以抽一个公共的SparkUtil来建 session能省不少启动时间。配置环境这件事掌握方法后一次成型后面写代码就会舒服很多。