大量的S3路径,为什么Hadoop需要这么长时间才能启动?
Why does Hadoop take so long to start with a large number of S3 paths?
我有一个 Hadoop 作业,它有大约 60k S3 输入路径。这项工作大约需要 45 分钟才能开始。同样的工作,只有 ~3k S3 输入路径几乎立即开始。
为什么有大量输入路径会导致作业启动时间过长?
FileInputFormat
在 MapReduce 初始化期间做的第一件事就是确定输入拆分。这是通过创建每个输入文件及其信息(例如文件大小)的列表来完成的。我想 60k API 次调用 S3 以获取文件信息的速度并不快。 45 分钟似乎非常慢 - 可能还有一些速率限制?
答案与 FileInputPath.addInputPath(...)
的实现方式有关。如果您查看源代码 here,您会发现它实际上进行了字符串连接以将所有这些路径保存到一个文件中。调用 addInputPaths(...)
只是调用 addInputPath
,所以那里没有节省。我最后打电话给 FileInputPath.setInputPaths(Job, Path[])
。这通过一次构建设置文件的那部分来跳过 60k+ 字符串连接。
如 climbage
所述,需要对 S3 进行 60k+ 次调用才能构建拆分。事实证明,S3 调用比字符串连接花费的时间更少。我的工作从开始需要 45 分钟减少到不到 20 分钟。
对于那些不想梳理源代码的人,这里是 FileInputFormat.addInputPath()
在 Hadoop 2.5.1 中的实现:
public static void addInputPath(Job job,
Path path) throws IOException {
Configuration conf = job.getConfiguration();
path = path.getFileSystem(conf).makeQualified(path);
String dirStr = StringUtils.escapeString(path.toString());
String dirs = conf.get(INPUT_DIR);
conf.set(INPUT_DIR, dirs == null ? dirStr : dirs + "," + dirStr);
}
Hadoop 2.5.1 中的 和 FileInputFormat.setInputPaths()
:
public static void setInputPaths(Job job,
Path... inputPaths) throws IOException {
Configuration conf = job.getConfiguration();
Path path = inputPaths[0].getFileSystem(conf).makeQualified(inputPaths[0]);
StringBuffer str = new StringBuffer(StringUtils.escapeString(path.toString()));
for(int i = 1; i < inputPaths.length;i++) {
str.append(StringUtils.COMMA_STR);
path = inputPaths[i].getFileSystem(conf).makeQualified(inputPaths[i]);
str.append(StringUtils.escapeString(path.toString()));
}
conf.set(INPUT_DIR, str.toString());
}
很抱歉重新打开一个旧问题,但我最近遇到了类似的问题。
它的核心是,在您的情况下,Hadoop 将对 AWS
进行 60K 次调用
要解决这个问题,可以使用通配符
FileInputFormat.addInputPath("path_to_a_folder/prefix*")
这将仅生成 1 次 AWS 调用以列出目录 path_to_a_folder
,然后按前缀过滤
我希望这对找到这个问题的人有所帮助
我有一个 Hadoop 作业,它有大约 60k S3 输入路径。这项工作大约需要 45 分钟才能开始。同样的工作,只有 ~3k S3 输入路径几乎立即开始。
为什么有大量输入路径会导致作业启动时间过长?
FileInputFormat
在 MapReduce 初始化期间做的第一件事就是确定输入拆分。这是通过创建每个输入文件及其信息(例如文件大小)的列表来完成的。我想 60k API 次调用 S3 以获取文件信息的速度并不快。 45 分钟似乎非常慢 - 可能还有一些速率限制?
答案与 FileInputPath.addInputPath(...)
的实现方式有关。如果您查看源代码 here,您会发现它实际上进行了字符串连接以将所有这些路径保存到一个文件中。调用 addInputPaths(...)
只是调用 addInputPath
,所以那里没有节省。我最后打电话给 FileInputPath.setInputPaths(Job, Path[])
。这通过一次构建设置文件的那部分来跳过 60k+ 字符串连接。
如 climbage
所述,需要对 S3 进行 60k+ 次调用才能构建拆分。事实证明,S3 调用比字符串连接花费的时间更少。我的工作从开始需要 45 分钟减少到不到 20 分钟。
对于那些不想梳理源代码的人,这里是 FileInputFormat.addInputPath()
在 Hadoop 2.5.1 中的实现:
public static void addInputPath(Job job,
Path path) throws IOException {
Configuration conf = job.getConfiguration();
path = path.getFileSystem(conf).makeQualified(path);
String dirStr = StringUtils.escapeString(path.toString());
String dirs = conf.get(INPUT_DIR);
conf.set(INPUT_DIR, dirs == null ? dirStr : dirs + "," + dirStr);
}
Hadoop 2.5.1 中的 和 FileInputFormat.setInputPaths()
:
public static void setInputPaths(Job job,
Path... inputPaths) throws IOException {
Configuration conf = job.getConfiguration();
Path path = inputPaths[0].getFileSystem(conf).makeQualified(inputPaths[0]);
StringBuffer str = new StringBuffer(StringUtils.escapeString(path.toString()));
for(int i = 1; i < inputPaths.length;i++) {
str.append(StringUtils.COMMA_STR);
path = inputPaths[i].getFileSystem(conf).makeQualified(inputPaths[i]);
str.append(StringUtils.escapeString(path.toString()));
}
conf.set(INPUT_DIR, str.toString());
}
很抱歉重新打开一个旧问题,但我最近遇到了类似的问题。 它的核心是,在您的情况下,Hadoop 将对 AWS
进行 60K 次调用要解决这个问题,可以使用通配符
FileInputFormat.addInputPath("path_to_a_folder/prefix*")
这将仅生成 1 次 AWS 调用以列出目录 path_to_a_folder
,然后按前缀过滤
我希望这对找到这个问题的人有所帮助