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

Python多进程Pool并行rsync备份脚本功能正常但报异常错误求助

问题根源分析与优化方案

首先,咱们来拆解你遇到的错误和脚本的问题:

为什么会出现change_dir failed错误?

  1. 并行执行顺序无保障:multiprocessing.Pool会并行处理所有任务,但任务的执行顺序是随机的。比如同步/Folder/New_folder-A/3A.txt的进程可能先启动,这时候/dest/Folder/New_folder-A目录还没被创建(因为同步New_folder-A目录的进程可能还没执行),rsync找不到目标目录就会报错。
  2. 重复同步任务冗余:你的get_pathlist函数同时添加了「目录」和「目录下的所有文件」到任务列表。这意味着rsync既要同步整个目录(包含所有子内容),又要单独同步每个文件。虽然最终目录同步会成功并覆盖文件同步的失败,但这种重复工作不仅浪费资源,还会触发大量不必要的错误日志。

为什么最终备份结果是正确的?

因为你同步目录的rsync任务(比如同步New_folder-A)会递归同步目录下的所有文件,即使单个文件的同步任务失败了,后续的目录同步还是会把文件正确复制到目标位置,所以最终结果看起来没问题,但过程中的错误确实存在风险(比如如果目录同步任务先失败,文件同步也失败,就会丢失数据)。


优化方案(两种选择)

方案1:只同步源目录的直接子项(推荐,最简洁高效)

rsync的-a参数已经包含递归同步(-r),所以完全没必要单独同步每个文件和子目录。只需要同步源目录下的直接子文件/目录,rsync会自动处理所有子内容,既减少任务数量,又从根源避免目录不存在的问题。

修改get_pathlist函数:

def get_pathlist(folder):
    pathlist = []
    # 仅获取源目录下的直接子项,让rsync递归处理内部内容
    for item in os.listdir(folder):
        # 直接子项的相对路径为空字符串,项目名为item本身
        pathlist.append(("", item))
    return pathlist

这样每个任务只同步source/下的一个直接子项到destination/,rsync会自动创建目标目录并递归同步所有子内容,既高效又不会有错误日志。

方案2:保留单个文件/目录同步,但解决顺序与目录创建问题

如果必须坚持同步每个单独的文件和目录,可以通过排序保证父目录优先执行+让rsync自动创建目标目录来解决错误:

  1. 修改get_pathlist,按目录深度排序,确保父目录先被处理:
def get_pathlist(folder):
    pathlist = []
    for root, dirs, files in os.walk(folder):
        # 计算当前路径相对于源目录的深度(根目录深度为0)
        depth = root[len(folder):].count(os.sep)
        for f in files:
            path = root[len(folder):]
            pathlist.append((depth, path, f))
        for d in dirs:
            path = root[len(folder):]
            pathlist.append((depth, path, d))
    # 按深度从小到大排序,父目录优先执行
    pathlist.sort(key=lambda x: x[0])
    # 转换回原格式的(path, item)元组
    return [(item[1], item[2]) for item in pathlist]
  1. 修改backup函数的rsync命令,添加--mkpath参数(rsync 3.0.0+支持),让rsync自动创建不存在的目标目录:
def backup(path):
    src = os.path.join(source, path[0], path[1])
    dest = os.path.join(destination, path[0])
    # 添加--mkpath自动创建目标目录
    subprocess.call(['rsync', '-azq', '--mkpath', src, dest])

额外优化建议

  • 去掉maxtasksperchild=1:这个参数会让每个进程只处理一个任务就销毁,增加进程创建/销毁的开销。除非你有内存泄漏的问题,否则建议删除该参数,让进程复用处理多个任务,提升效率。
  • 可以考虑用subprocess.check_call替代subprocess.call:这样如果rsync执行失败会抛出异常,方便你捕获并处理错误(比如重试),而不是默默忽略。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 11:47:30