NFS挂载目录间rsync拷贝370GB大文件完成但目标文件丢失问题
拷贝失败核心原因
- 未校验命令执行结果与错误输出:使用的
subprocess.call仅执行命令但未捕获stderr错误流、也未校验命令返回码,rsync执行过程中所有报错都会被直接丢弃,是看不到错误日志的直接原因。 - rsync进度100%不代表最终写入成功:rsync的进度统计仅表示用户态层面已将全部文件数据提交给操作系统内核,不代表数据已真正写入目标存储。默认配置下rsync会先在目标目录创建
.开头的隐藏临时文件写入数据,数据写完后才会执行重命名、元数据写入、落盘操作,这几个步骤任意一步失败,rsync都会主动删除临时文件并返回非0退出码。看到的100%进度只是临时文件写入完成,后续步骤失败后临时文件被清理,自然找不到目标文件。 - NFS挂载特性放大失败概率:
- NFS默认会使用客户端页缓存,大文件写入时数据会先缓存在客户端内存,未及时刷到NFS服务端时如果出现NFS连接超时、挂载点重连、客户端内存回收异常,缓存数据会直接丢失。
- 多数NFS服务端默认开启
root_squash配置,若用root用户运行脚本,root在NFS挂载点的权限会被映射为匿名用户,后续重命名临时文件、修改文件元数据时会触发权限错误。 - 添加的
--whole-file参数会关闭rsync的块级校验逻辑,NFS层出现静默数据损坏时rsync无法感知。
- 未做最终一致性校验:脚本没有在命令执行后主动检查目标文件是否存在、大小/校验和是否匹配,无法感知写入失败的情况。
可靠大文件拷贝实现方案
基于rsync的优化方案(性能最优,适合TB级大文件)
- 调整rsync参数:新增
--fsync参数强制rsync在数据写完后将文件缓存刷到持久存储再返回成功,新增--partial参数保留未完成的临时文件支持断点续传,不需要同步权限属性时新增--no-perms --no-owner --no-group参数避免NFS权限映射导致的元数据写入失败,显式指定--temp-dir为目标NFS挂载点路径,避免跨设备重命名失败。调整后的rsync参数示例:
rsync_cmd = [ "rsync", "-avu", "--progress", "--fsync", "--partial", "--temp-dir", file_dst, file_src, file_dst ]
- 正确处理子进程输出与返回值:替换
subprocess.call为subprocess.run,捕获标准输出、错误流,显式校验返回码,非0时直接打印错误日志,示例代码:
import subprocess result = subprocess.run( rsync_cmd, capture_output=True, text=True ) if result.returncode != 0: print(f"rsync执行失败,错误信息:{result.stderr}") # 补充异常处理、重试逻辑
- 执行后校验:rsync执行完成后,主动检查目标路径下文件是否存在,对比源文件和目标文件的大小,必要时计算SHA256校验和确认文件完全一致。
- NFS挂载优化:挂载NFS时添加
soft,timeo=600,retrans=3参数,避免NFS异常时进程永久挂死,根据业务需求关闭root_squash或配置正确的权限映射。
Python原生实现方案(无外部依赖,可控性高)
- 按16MB-64MB的固定块大小(匹配NFS最优读写块大小)分块读取源文件,写入目标临时文件,每写若干块主动调用
os.fsync()刷盘,避免缓存堆积。 - 支持断点续传:写入前检查临时文件大小,从已写入的偏移位置继续传输,无需每次从头拷贝。
- 全部数据写完后,调用
os.fsync()强制刷盘,对比源文件和目标文件的大小、校验和,确认一致后再将临时文件重命名为正式文件名,避免出现半写文件。 - 全链路捕获
OSError、IOError等异常,记录错误上下文(路径、偏移量、错误码),方便排查问题。
内容的提问来源于stack exchange,提问作者Yagel
相关产品推荐
相关产品推荐

