我的工作流程如下:
我正在处理大量数据。我有一个MapFile
需要缓存。这个文件的大小现在是1 GB,但我希望它最终会增长。
MapFile的内容将是这样的:
12345,45464 192.34.23.1
33214,45321 123.45.32.1
map-phase
中,我处理TextInputFormat
中输入文件中的每条记录。
我解析该行(由标记拆分)并检索前两个标记,即token1和token2。如果(token1,token2)对不在缓存文件中,那么我会进行API调用,获取信息,在缓存中保留(如果可能)并继续处理。
private Parser parser = new customParser();
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
parser.parse(value);
Pair pair = new Pair();
pair.setFirst(parser.getFirst());
pair.setSecond(parser.getSecond());
IP ip = null;
//here is the catch
//check if pair exists in cache
if cache.contains(pair){
ip=cache.get(pair);
}
else {
ip=getFromAPI(pair);//This does API call outside network.
cache.put(pair,ip);
}
context.write(pair,ip);
}
}
我在这里看到的主要问题是
如何在所有节点的缓存中获取大文件。 DistributedCache的工作原理是将文件复制到本地节点。但由于这个文件比较大,这里涉及网络流量,对于我的日常工作,我不想继续分发它。
如何有效地查找MapFile(缓存),整个mapFile将不会在内存中。
如何写入这个我的缓存的MapFile。
由于
答案 0 :(得分:0)
我认为有三种方法可以解决这个问题,最好的方法取决于缓存文件的增长方式。
如果您不希望缓存文件增长很多并且它总是适合内存而不会妨碍其他应用程序或MapReduce作业,您可以将其放入HDFS cache。自Hadoop 2.3.0起支持此功能:
HDFS缓存允许用户在HDFS中显式缓存某些文件或目录。然后,DataNodes将通过使用mmap和mlock来缓存堆外内存中的相应块。缓存后,Hadoop应用程序可以查询缓存块的位置,并将其任务放在内存位置。最后,当内存本地时,应用程序可以使用新的零拷贝读取API来读取缓存数据而无需额外开销。
如果在缓存文件增长时无法将其保存在内存中,则最后两个选项更合适:
This answer建议将您的缓存文件放入HDFS increasing the replication count并使用FileSystem API阅读。这仍然会导致非本地副本的网络通信,但希望少于传输到DistributedCache中的所有节点。 FileSystem API还允许您附加到现有文件,以便您更新文件。
如果缓存文件增长太多以至于存在额外复制存在问题,您可能会考虑将其作为第一个映射步骤的一部分进行检索。
例如,您可以将缓存文件和要处理的文件作为输入映射到映射器,并且对于两个输入都映射令牌对。在reduce步骤中,如果一个令牌对从缓存文件和处理过的文件中都有一行,并在另外两种情况下输出相应的缓存行,则不会输出任何内容来构建新的缓存文件。