使用MultipleOutputs报File already exists错误

在reduce任务看到第一个任务因为某些原因失败了,但后续的任务都一起失败,后续的任务报File already exists错误。 原因是MultipleOutputs.write的时候可以指定输出的别名或者绝对路径,如果写的是绝对路径,目录会马上生效,MR的output commit机制会失效(先输出到临时目录,然后最后移动回正式目录)。当任务第一次失败后,第二次重试还是存在着那个文件会报错退出。 MR会在任务失败的时候清理输出,但仅限于taskAttemptPath,不会清理其他产生的文件。

@Private
  public void abortTask(TaskAttemptContext context, Path taskAttemptPath) throws IOException {
    if (hasOutputPath()) { 
      context.progress();
      if(taskAttemptPath == null) {
        taskAttemptPath = getTaskAttemptPath(context);
      }
      FileSystem fs = taskAttemptPath.getFileSystem(context.getConfiguration());
      if(!fs.delete(taskAttemptPath, true)) {
        LOG.warn("Could not delete "+taskAttemptPath);
      }
    } else {
      LOG.warn("Output Path is null in abortTask()");
    }
  }

解决方法

想办法在第二次跑之前清理文件。最终输出的文件名是用户指定路径+数字,那个数字不太好从哪里获取到,另外如果开了推测执行,推测执行的任务也会失败。 解决的方法还是不要用绝对路径输出,按key输出文件,最后如果需要分开目录的时候再在client那边把文件移动过去。  

相关issue

这个issue只是指出如果用绝对路径,output committing的机制会失效,加了个警告注释
https://issues.apache.org/jira/browse/MAPREDUCE-6357

updatedupdated2023-12-062023-12-06