Databricks调用dbutils.fs.mv()抛FileNotFoundException但文件实际存在
问题成因
- 首先可以确定不是Spark写入未完成导致的:
df.write.parquet是Spark的行动算子,只有当所有数据文件和元数据文件全部写入完成后,代码才会往下执行到dbutils的操作,你手动能查到文件存在也能佐证这一点。 - 核心原因是ADLS Gen2的元数据最终一致性特性:ADLS Gen2是对象存储适配了文件系统语义,新写入的文件尤其是Spark生成的
_committed_xxx、_SUCCESS这类带下划线前缀的隐藏元文件,元数据同步会有毫秒级到秒级的延迟。虽然前端门户已经能查到文件,但是API请求路由到的存储节点还没同步到该文件的元数据,就会抛出404不存在的错误。 - 另外dbutils的
fs.mv递归操作在ADLS Gen2上的实现逻辑是遍历目录下所有文件逐个执行rename,刚好遍历到还未完成元数据同步的隐藏元文件时,就会触发你遇到的报错。
可行的解决方案
你可以根据场景选择适配方案:
- 写入完成后加短暂等待,再执行mv操作
在df.write.parquet(path_new)和第一个mv操作之间加1-3秒的休眠,给ADLS留够元数据同步的时间,大部分场景下都能解决问题:
import time # 写入完成后加等待 df.write.parquet(path_new) time.sleep(2) # 可根据实际目录文件数量调整时长
- 跳过隐藏元文件的移动
如果你不需要保留Spark的写入元数据,移动前先过滤掉_开头的隐藏文件,只移动实际的parquet数据文件,也能规避该问题。 - 改用ADLS原生SDK执行目录rename
如果还是偶发报错,可以不用dbutils的mv,直接调用azure-storage-file-datalake SDK的目录rename方法,原子性更高,对隐藏文件的兼容性更好。
内容的提问来源于stack exchange,提问作者Hanebambel
相关产品推荐
相关产品推荐

