You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows环境下用Staging S3A Committer写入S3时遇UnsatisfiedLinkError

解决Windows本地Spark写入Parquet到S3的UnsatisfiedLinkError问题

我来帮你排查这个问题,这个错误在Windows本地跑Spark+S3+Parquet的场景里挺常见的,咱们一步步来分析解决:

问题根源分析

这个java.lang.UnsatisfiedLinkError: org.apache.hadoop.io.nativeio.NativeIO$POSIX.stat错误,本质是Hadoop的Native本地库在Windows环境下缺失或不兼容导致的:

  • Hadoop的NativeIO类依赖系统级的本地库(比如Windows下的.dll文件)来处理POSIX风格的文件操作,但Windows本身不支持POSIX接口;
  • Parquet的提交流程恰好触发了调用这个本地库的代码路径,而ORC/CSV的提交逻辑没走到这一步,所以没报错;
  • 另外你用的spark-hadoop-cloud版本和Spark2.4.5、Hadoop3.2.1不匹配,也加剧了兼容性冲突。

解决方案

方案1:快速绕开Native库依赖(临时解决)

直接强制Hadoop用纯Java实现代替本地Native库,在SparkSession配置里添加这两行:

.config("spark.hadoop.io.nativeio.posix.enabled", "false")
.config("spark.hadoop.hadoop.native.lib", "false")

这样Hadoop就不会尝试调用POSIX本地方法,直接绕开链接错误,适合快速验证功能。

方案2:补充Hadoop Windows本地库(长期解决)

如果想彻底解决Native库依赖问题,需要下载适配Hadoop3.2.1的Windows本地库:

  1. 下载包含hadoop.dll、winutils.exe等文件的Hadoop3.2.1 Windows Native包;
  2. 把这些文件放到项目的本地目录,比如src/main/resources/hadoop-native/;
  3. 在Spark配置里指定本地库路径:
.config("spark.hadoop.hadoop.native.lib.path", "src/main/resources/hadoop-native/")

或者设置系统环境变量HADOOP_HOME指向存放这些文件的目录,并把%HADOOP_HOME%\bin加到系统PATH中。

方案3:修正SBT依赖版本冲突

你当前用的spark-hadoop-cloud版本(2.3.2.3.1.0.6-1)和Spark2.4.5、Hadoop3.2.1版本不兼容,建议调整:
修改SBT的libraryDependencies:

scalaVersion := "2.11.11"
libraryDependencies ++= Seq(
  "org.apache.spark" %% "spark-sql" % "2.4.5",
  // 移除旧的不兼容版本,换成和Spark2.4.5匹配的版本
  "org.apache.spark" %% "spark-hadoop-cloud" % "2.4.5",
  "org.apache.hadoop" % "hadoop-client" % "3.2.1",
  "org.apache.hadoop" % "hadoop-common" % "3.2.1",
  "org.apache.hadoop" % "hadoop-aws" % "3.2.1",
  "com.amazonaws" % "aws-java-sdk-bundle" % "1.11.704"
)
// 其他dependencyOverrides和resolvers保持不变

如果不需要额外的cloud功能,甚至可以直接移除spark-hadoop-cloud依赖——Hadoop3.2.1自带的S3A Committer已经支持目录提交器功能。

方案4:简化Parquet提交器配置

你的配置里有些冗余项可能引发冲突,尝试简化为:

val spark = SparkSession.builder()
  .appName("test-run-s3a-commiters")
  .master("local[*]")
  .config("spark.hadoop.fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem")
  .config("spark.hadoop.fs.s3a.endpoint", "s3.eu-central-1.amazonaws.com")
  .config("spark.hadoop.fs.s3a.aws.credentials.provider", "com.amazonaws.auth.profile.ProfileCredentialsProvider")
  .config("spark.hadoop.fs.s3a.connection.maximum", "100")
  .config("spark.hadoop.fs.s3a.committer.name", "directory")
  .config("spark.hadoop.fs.s3a.committer.magic.enabled", "false")
  .config("spark.hadoop.fs.s3a.committer.staging.conflict-mode", "append")
  .config("spark.hadoop.fs.s3a.committer.staging.unique-filenames", "true")
  .config("spark.hadoop.fs.s3a.committer.staging.abort.pending.uploads", "true")
  .config("spark.hadoop.fs.s3a.buffer.dir", "./tmp/")
  // 改成本地可写的明确路径,避免HDFS解析问题
  .config("spark.hadoop.fs.s3a.committer.staging.tmp.path", "./tmp/s3a-staging/")
  .config("spark.hadoop.mapreduce.outputcommitter.factory.scheme.s3a", "org.apache.hadoop.fs.s3a.commit.S3ACommitterFactory")
  .config("spark.sql.sources.commitProtocolClass", "org.apache.spark.internal.io.cloud.PathOutputCommitProtocol")
  .config("spark.sql.parquet.output.committer.class", "org.apache.spark.internal.io.cloud.BindingParquetOutputCommitter")
  // 添加方案1的配置(如果选方案1的话)
  .config("spark.hadoop.io.nativeio.posix.enabled", "false")
  .config("spark.hadoop.hadoop.native.lib", "false")
  .getOrCreate()

重点是把临时路径改成本地可写的明确目录,移除可能冲突的MapReduce配置项。


验证

修改配置后重新运行你的测试代码,应该能正常将Parquet数据写入S3了。

内容的提问来源于stack exchange,提问作者Nementaarion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 15:42:53