繁体   English   中英

用Java计算+2000万条记录的统计信息

[英]Calculate statistics on +20millions records in Java

我有csv文件(600 MB)和2000万行。 我需要读取所有这些数据,从中创建Java对象列表,并在对象字段上计算一些指标,例如平均值,中位数,最大值,总和和其他统计信息。 用Java做到这一点的最佳方法是什么? 我尝试了简单的.forEach循环,并花了一段时间(20分钟)对其进行迭代。

更新:我使用BufferReader读取数据并将csv文件转换为某些Java类的对象列表。 非常快。 它在forEach循环中停留了20分钟,在该循环中,我尝试遍历这2000万个对象列表,并将它们分为3个列表,具体取决于当前对象中的值。 因此,基本上,我遍历整个列表一次,并且具有if / else条件,在该条件下,我检查对象中的某些字段是否等于“ X”,“ Y”或“ Z”,并根据答案将其分开将这20万条记录分成3个列表。

然后,对于这三个列表,我需要计算不同的统计数据:例如中位数,平均值,总和等

广泛处理超过600Mb的数据量后,我可以提出以下两种说法:

  • 600Mb 并不是大量数据,尤其是当我们谈论表格数据时;
  • 这些数量与BigData无关,实际上可以在内存中的常规硬件上轻松处理,这是最快的选择。

但是,您应该做的是确保将数据读入列式连续数组,并使用直接在列式数据的那些连续数组上运行的方法。

因为它是一个csv文件,是按行存储的,所以最好将它按块读取为字节数组,然后将其解析为按列的预分配表示形式。

将600Mb的块读入SSD的内存中大约需要几秒钟,对其进行解析将取决于您的算法(但是必须能够在该结构中立即查找是至关重要的)。 在内存方面,您将使用大约600Mb的三倍,但是对于16Gb机器而言,这无疑是个明智的选择。

因此,不要急于使用SQL或切片文件,也不要将每个单元都实例化为Java对象。 也就是说,在这种例外情况下 ,您不需要Java对象列表 ,而需要double[]等。尽管您预先分配了确切的大小,但是可以使用ArrayList 其他标准收藏品将杀死您。

说了这么多,我宁愿推荐pythonnumpy来完成任务,而不是Java。 Java在对象方面很有用,而在连续内存块和相应的操作方面则不如Java。 C++甚至可以做到R

我强烈建议不要将全部600MB加载到RAM中并将其用作Java对象。 正如您所说的,这需要花费很多时间。

您可以做什么:

使用SQL:将数据转换为数据库,然后在该数据库上执行搜索查询。 不要循环访问RAM中的所有对象。 这将使您的应用程序性能很差。

SQL经过优化,可处理大量数据并对其执行查询。

阅读有关Java中的数据库管理的更多信息: JDBC基础

听起来就像您在向列表中添加内容时,程序只是耗尽了内存。 如果您接近分配给JVM的内存限制,则大部分时间将由垃圾收集器花费,以尽其所能防止内存不足。

您应该使用快速 CSV库(例如univocity解析器)遍历每一行并执行所需的计算,而不必将所有内容存储在内存中。 像这样使用它:

CsvParserSettings parserSettings = new CsvParserSettings(); //configure the parser
parserSettings.selectFields("column3", "column1", "column10"); //only read values from columns you need

CsvParser parser = new CsvParser(parserSettings);

//use this if you just need plain strings
for(String[] row : parser.iterate(new File("/path/to/your.csv"))){
    //do stuff with the row
}

//or use records to get values ready for calculation
for(Record record : parser.iterateRecords(new File("/path/to/your.csv"))){
    int someValue = record.getInt("columnName");
    //perform calculations
}

如果出于某种原因您需要多次遍历所有行,只需将数据存储在庞大的列表中即可。 在这种情况下,可以使用-Xms8G -Xmx8G类的程序为程序分配更多的内存。 请记住, ArrayList的大小不能超过Integer.MAX_VALUE因此即使您有足够的内存,这也是下一个限制。

如果您确实需要一个列表,则可以使用如下所示的解析器:

List<Record> twentyMillionRecords = parser.parseAllRecords(new File("/path/to/your.csv"), 20_000_000);

否则,最好的选择是根据需要多次运行解析器。 我建议的解析器每次都需要花费几秒钟来浏览文件。

希望这可以帮助

免责声明:我是这个图书馆的作者。 它是开源和免费的(apache 2.0许可)

我敢打赌,大部分时间都花在读取数据上。 拥有BufferedReader应该可以大大加快速度。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM