为什么使用一条很长的单行作为输入的处理文件会给出不同数量的记录?

时间:2015-04-17 02:23:22

标签: java apache-spark

我使用Spark 1.2.1(在local模式下)从文件中提取和处理日志信息。

文件大小可能超过100Mb。该文件包含很长的单行,因此我使用正则表达式将此文件拆分为日志数据行。

MyApp.java

JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<String> txtFileRdd = sc.textFile(filename);
JavaRDD<MyLog> logRDD = txtFileRdd.flatMap(LogParser::parseFromLogLine).cache();

LogParser.java

public static Iterable<MyLog> parseFromLogLine(String logline) {
        List<MyLog> logs = new LinkedList<MyLog>();
        Matcher m = PATTERN.matcher(logline);
        while (m.find()) {          
            logs.add(new MyLog(m.group(0)));            
        }   
        System.out.println("Logs detected " + logs.size());
        return logs;
}

已处理文件的实际大小约为100 Mb,实际上包含323863个日志项。

当我使用Spark从文件中提取我的日志项时,我得到455651 [logRDD.count()]日志项不正确。

我认为这是因为文件分区,检查输出我看到以下内容:

Logs detected 18694  
Logs detected 113104  
Logs detected 323863

总和为455651

所以我看到我的分区彼此合并,保留了重复的项目,我想阻止这种行为。

解决方法是使用repartition(1),如下所示:

txtFileRdd.repartition(1).flatMap(LogParser::parseFromLogLine).cache();

这确实给了我想要的结果323863,但我怀疑它对表现有好处。

如何更好地处理性能?

1 个答案:

答案 0 :(得分:3)

默认情况下,分区是基于行的。看来,当有一条非常长的线时,这会以一种有趣的方式失败。你可以考虑为此提交一个错误(也许已经有了)。

拆分由Hadoop文件AP​​I执行,特别是TextInputFormat类。一种选择是指定您自己的InputFormat(可能包括整个解析器)并使用sc.hadoopFile

另一种选择是通过textinputformat.record.delimiter

设置不同的分隔符
// Use space instead of newline as the delimiter.
sc.hadoopConfiguration.set("textinputformat.record.delimiter", " ")