我在同一个文件夹中有数百到数千个输入文件(.csv)和元数据文件(.json)。 $ HDFS_ROOT / inputFolder
//输入数据.csv文件
input_1.csv,input_2.csv..input_N.csv
//输入元数据.json文件
input_1.json,input_2.json..input_N.json
有人可以给我一些关于如何让每个映射器获取文件对的提示,即整个输入文件(.csv)及其元数据文件(.json)。
注意:input_i.csv和input_i.json应该转到同一个映射器,以便输入及其元数据都有意义进行验证。
我尝试了什么: 我尝试分别使用从FileInputFormat和RecordReader扩展的WholeFileInputFormat和WholeFileRecordReader。这仅适用于.csv文件。此外,我将.json文件放入分布式缓存中以供映射器访问。这不是一个好的解决方案。
答案 0 :(得分:1)
在不使用昂贵的Reducer的情况下解决此问题的关键是InputSplits。每个InputFormat都有方法 getSplits ,单个分割是单个Mapper的输入,有与InputSplits一样多的Mappers。 在映射器中,可以访问InputSplit实例:
@Override
protected void setup(Context context) throws IOException, InterruptedException {
System.out.println("TRACE 1 " + context.getConfiguration().getClass().getName());
System.out.println("TRACE 2 " + context.getTaskAttemptID().toString());
System.out.println("TRACE 3 " + context.getInputSplit().toString());
}
基于此,我过去使用了3种方法:
1) context.getInputSplit()返回 FileSplit 的实例,该实例具有 Path getPath()方法。但是你必须注意 CombineFileSplit 和 TaggedInputSplit ,这可以包围 FileSplit 。使用 CombineFileSplit ,如果不覆盖 CombineFileInputFormat.pools 周围的默认行为,则可能会在同一个Mapper中混合使用不同结构的记录而无法区分它们; < / p>
2)更简单的方法是使用 context.getInputSplit()。toString(),返回的字符串将包含 InputSplit 所附加的路径,效果很好使用 MultipleInputs ,但不能使用 CombineFileInputFormat 。它有点脏,因为你受 toString()方法的支配,不会推荐它用于生产系统,但对于快速原型来说已经足够了;
3)要定义您自己的代理 InputFormat 和 InputSplit 的实现,类似于MultipleInputs方法使用的,它依赖于 DelegatingInputFormat 包围可以读取数据的 InputFormat 的 InputSplit ,但将它们放在 TaggedInputSplit 中,请参阅源代码。在您的情况下,您可以在自己的 InputFormat 和 InputSplits 中隐藏元数据逻辑,并使Mappers无需知道如何将文件与元数据匹配。您还可以直接将输入路径关联到元数据,而无需依赖命名约定。这种方法非常适合生产系统。