使用Jackson jackson-dataformat-csv库(CsvSchema、CsvMapper等)有效地解析CSV文件是否可能,即使该文件的不同行具有不同的模式?
我强调“有效”是因为我有非常大的文件(> 100,000,000行)需要解析,应用程序对性能敏感。 如果每一行中的每个列都实例化一个新的Object/String,GC将放弃我。 我希望尽可能使用原语,例如,31作为int返回。
如果可以,建议采取什么方法?
FYI,文件模式如下:
我强调“有效”是因为我有非常大的文件(> 100,000,000行)需要解析,应用程序对性能敏感。 如果每一行中的每个列都实例化一个新的Object/String,GC将放弃我。 我希望尽可能使用原语,例如,31作为int返回。
如果可以,建议采取什么方法?
FYI,文件模式如下:
ROW_TYPE | ...
。 即,每一行的第一列表示列类型,并且对于给定的列类型,模式始终相同。 然后其余列在行之间不同,具体取决于它们的列类型。 例如:1|"text1a"|2|3|4|true|"text2a"
2|3|"text"
1|"text1b"|5|6|7|false|"text2b"
目前我使用neo4j-csv
库。
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-csv</artifactId>
<version>2.2-SNAPSHOT</version>
</dependency>
它具有极高的性能并且产生非常少的垃圾。此外,它支持逐列读取条目,并在每次读取时指定类型-虽然更为复杂,但也更加灵活。参考用法如下:
// do once per file
CharSeeker charSeeker = new BufferedCharSeeker(...), bufferSize);
int columnDelimiter = '|';
Extractors extractors = new Extractors();
Mark mark = new Mark();
// do repeatedly while parsing
charSeeker.seek(mark, columnDelimiters))
int eventType = charSeeker.extract(mark, extractors.int_()).intValue();
switch (eventType) {
case 1: // parse row type 1
break;
case 2: // parse row type 2
break;
...
...
}
我考虑转换的原因是希望减少项目的依赖关系,而且由于我已经使用Jackson进行JSON处理,所以在CSV处理方面也使用它是有意义的(性能/功能等待进一步检验)。
CharSeeker
解析器,我在1阶段进行基于行的解析。我将此方法与正则表达式split()进行了比较,即返回一个字符串数组,然后解析这些字符串。生成的垃圾数量约为100倍。每个列的1个字符串加上一个string[]和任何已解析的字符串。结果性能差了近6倍,GC行为也无法预测 - 当您在>> 1,000,000个对象/秒的速度下生成>> 100,000,000个对象时,GC需要对它们进行处理,即使它们是短暂的。 - Alex AverbuchString[]
的速度快慢。根据我所做的基准测试,CSV后端似乎与JSON相比没有不合理的开销,这就是为什么我会感到惊讶,如果看到超过2倍的差异。 - StaxManMappingIterator.nextValue()
都会返回一个新的对象,因此会产生大量的对象分配。即使是短暂存在的对象也需要被收集,而收集并不是免费的。此外,通过基准测试和分析,我发现当我以高速解析大型文件时,堆栈非常快地增长(带有垃圾、未使用的对象)。最小化分配可以最小化GC,这就是我想要的。 - Alex Averbuch