我研究了NLineInputFormat的getSplitsForFile()fn。我发现为输入文件&创建了一个InputStream。然后每n行创建一次迭代和分割。 它有效吗?特别是当在启动映射器任务之前在1个节点上发生此读取操作时。如果1有5GB的文件怎么办?基本上它意味着文件数据被寻找两次,一次是在分割创建过程中。从mapper任务中读取一次。 如果这是一个瓶颈,hadoop的工作如何覆盖这个?
public static List<FileSplit> getSplitsForFile(FileStatus status,
Configuration conf, int numLinesPerSplit) throws IOException {
List<FileSplit> splits = new ArrayList<FileSplit> ();
Path fileName = status.getPath();
if (status.isDirectory()) {
throw new IOException("Not a file: " + fileName);
}
FileSystem fs = fileName.getFileSystem(conf);
LineReader lr = null;
try {
FSDataInputStream in = fs.open(fileName);
lr = new LineReader(in, conf);
Text line = new Text();
int numLines = 0;
long begin = 0;
long length = 0;
int num = -1;
<!-- my part of concern start -->
while ((num = lr.readLine(line)) > 0) {
numLines++;
length += num;
if (numLines == numLinesPerSplit) {
splits.add(createFileSplit(fileName, begin, length));
begin += length;
length = 0;
numLines = 0;
}
}
<!-- my part of concern end -->
if (numLines != 0) {
splits.add(createFileSplit(fileName, begin, length));
}
} finally {
if (lr != null) {
lr.close();
}
}
return splits;
}
编辑以将我的用例提供给clément-mathieu
我的数据集是每个2gb的大输入文件。文件中的每一行代表一条需要插入数据库表的记录(在我的例子中是cassandra) 我想将数据库的批量事务限制为每个n行。 我已经使用nlineinputformat成功完成了这项工作。我唯一担心的是,是否存在可能出现在生产中的隐藏性能瓶颈。
答案 0 :(得分:1)
基本上它意味着文件数据被寻找两次,一次是在分割创建过程中。从mapper任务中读取一次。
是
此InputFormat
的目的是为每个N行创建一个分割。计算拆分边界的唯一方法是读取此文件并查找换行符。这项操作成本很高,但如果这是您所需要的,则无法避免。
如果这是一个瓶颈,hadoop的工作如何覆盖这个?
不确定理解这个问题。
NLineInputFormat不是默认的InputFormat,很少有用例需要它。如果您阅读该类的javadoc,您将看到此类主要用于将参数提供给令人尴尬的并行作业(=“小”输入文件)。
大多数InputFormat不需要读取文件来计算拆分。它们通常使用硬性规则,例如拆分应为128MB 或每个HDFS块的一个拆分,并且RecordReaders将处理实际的开始/拆分结束偏移。
如果NLineInputFormat.getSplitsForFile
的费用是个问题,我会真的回顾为什么我需要使用这个InputFormat
。您要做的是限制映射器中业务流程的批量大小。使用NLineInputFormat
为每N行创建一个映射器,这意味着映射器永远不会执行多个批量事务。您似乎不需要此功能,您只想限制批量事务的大小,但不关心映射器是否按顺序执行其中几个。所以你要支付你发现的代码的成本作为回报。
我会使用TextInputFormat
并在映射器中创建批处理。在伪代码中:
setup() {
buffer = new Buffer<String>(1_000_000);
}
map(LongWritable key, Text value) {
buffer.append(value.toString())
if (buffer.isFull()) {
new Transaction(buffer).doIt()
buffer.clear()
}
}
cleanup() {
new Transaction(buffer).doIt()
buffer.clear()
}
默认情况下,每个HDFS块都会创建一个映射器。如果您认为这太多或太少,mapred.(max|min).split.size
变量允许增加或减少并行度。
基本上,虽然方便的NLineInputFormat
对于你需要的东西来说太精细了。你可以使用TextInputFormat
和*.split.size
一起玩,但不需要读取文件来创建分割。