惰性 CSV 过滤/解析 - 提高性能
Lazy CSV Filtering / Parsing - Increasing Performance
延迟过滤 CSV 文件
我需要过滤数以百万计的日志记录,存储为大量 CSV 文件。记录的大小大大超过了我的可用内存,所以我想采用一种懒惰的方法。
Java 8 个流 API
有了 jdk8
,我们就有了 Streams API,它与 Apache commons-csv
搭配使用,让我们可以轻松完成此任务。
public class LazyFilterer {
private static Iterable<CSVRecord> getIterable(String fileName) throws IOException {
return CSVFormat
.DEFAULT
.withFirstRecordAsHeader()
.parse(new BufferedReader(new FileReader(fileName)));
}
public static void main(String[] args) throws Exception {
File dir = new File("csv");
for (File file : dir.listFiles()) {
Iterable<CSVRecord> iterable = getIterable(file.getAbsolutePath());
StreamSupport.stream(iterable.spliterator(), true)
.filter(c -> c.get("API_Call").equals("Updates"))
.filter(c -> c.get("Remove").isEmpty())
.forEach(System.out::println);
}
}
}
性能
这张来自 VisualVM 的图表显示了使用比上面显示的更复杂的过滤管道1 解析 2.3 GB CSV 文件期间的内存使用情况。
可以看到,随着过滤的进行,内存占用基本保持不变2。
你能找到另一种方法来更快地完成同样的任务,同时又不增加代码的复杂性吗?
欢迎任何语言,Java不一定是首选!
脚注
[1] - 例如对于在 "API_Call"
上匹配的每个 CSVRecord
,我可能需要进行一些 JSON 反序列化并在此之后进行额外的过滤,或者甚至为某些记录创建一个对象以促进额外的计算。
[2] - 图表开头的空闲时间是 System.in.read()
,用于确保 VisualVM 在计算开始前完全加载。
这对于仅 2.3GB 的数据来说太可怕了,我可以建议您尝试使用 uniVocity-parsers 以获得更好的性能吗?试试这个:
CsvParserSettings settings = new CsvParserSettings();
settings.setHeaderExtractionEnabled(true); // grabs headers from input
//select the fieds you are interested in. The filtered ones get in front to make things easier
settings.selectFields("API_Call", "Remove"/*, ... and everything else you are interested in*/);
//defines a processor to filter the rows you want
settings.setProcessor(new AbstractRowProcessor() {
@Override
public void rowProcessed(String[] row, ParsingContext context) {
if (row[0].equals("Updates") && row[1].isEmpty()) {
System.out.println(Arrays.toString(row));
}
}
});
// create the parser
CsvParser parser = new CsvParser(settings);
//parses everything. All rows will be sent to the processor defined above
parser.parse(file, "UTF-8");
我知道它不起作用,但我花了 20 秒 来处理我创建的一个 4 GB 文件来测试它,同时消耗 整个时间少于 75mb 内存。从您的图形看来,对于较小的文件,您当前的方法似乎需要 1 分钟,并且需要 10 倍的内存。
试试这个例子,相信会有很大帮助。
免责声明,我是这个库的作者,它是开源且免费的(Apache 2.0 许可)
延迟过滤 CSV 文件
我需要过滤数以百万计的日志记录,存储为大量 CSV 文件。记录的大小大大超过了我的可用内存,所以我想采用一种懒惰的方法。
Java 8 个流 API
有了 jdk8
,我们就有了 Streams API,它与 Apache commons-csv
搭配使用,让我们可以轻松完成此任务。
public class LazyFilterer {
private static Iterable<CSVRecord> getIterable(String fileName) throws IOException {
return CSVFormat
.DEFAULT
.withFirstRecordAsHeader()
.parse(new BufferedReader(new FileReader(fileName)));
}
public static void main(String[] args) throws Exception {
File dir = new File("csv");
for (File file : dir.listFiles()) {
Iterable<CSVRecord> iterable = getIterable(file.getAbsolutePath());
StreamSupport.stream(iterable.spliterator(), true)
.filter(c -> c.get("API_Call").equals("Updates"))
.filter(c -> c.get("Remove").isEmpty())
.forEach(System.out::println);
}
}
}
性能
这张来自 VisualVM 的图表显示了使用比上面显示的更复杂的过滤管道1 解析 2.3 GB CSV 文件期间的内存使用情况。
可以看到,随着过滤的进行,内存占用基本保持不变2。
你能找到另一种方法来更快地完成同样的任务,同时又不增加代码的复杂性吗?
欢迎任何语言,Java不一定是首选!
脚注
[1] - 例如对于在 "API_Call"
上匹配的每个 CSVRecord
,我可能需要进行一些 JSON 反序列化并在此之后进行额外的过滤,或者甚至为某些记录创建一个对象以促进额外的计算。
[2] - 图表开头的空闲时间是 System.in.read()
,用于确保 VisualVM 在计算开始前完全加载。
这对于仅 2.3GB 的数据来说太可怕了,我可以建议您尝试使用 uniVocity-parsers 以获得更好的性能吗?试试这个:
CsvParserSettings settings = new CsvParserSettings();
settings.setHeaderExtractionEnabled(true); // grabs headers from input
//select the fieds you are interested in. The filtered ones get in front to make things easier
settings.selectFields("API_Call", "Remove"/*, ... and everything else you are interested in*/);
//defines a processor to filter the rows you want
settings.setProcessor(new AbstractRowProcessor() {
@Override
public void rowProcessed(String[] row, ParsingContext context) {
if (row[0].equals("Updates") && row[1].isEmpty()) {
System.out.println(Arrays.toString(row));
}
}
});
// create the parser
CsvParser parser = new CsvParser(settings);
//parses everything. All rows will be sent to the processor defined above
parser.parse(file, "UTF-8");
我知道它不起作用,但我花了 20 秒 来处理我创建的一个 4 GB 文件来测试它,同时消耗 整个时间少于 75mb 内存。从您的图形看来,对于较小的文件,您当前的方法似乎需要 1 分钟,并且需要 10 倍的内存。
试试这个例子,相信会有很大帮助。
免责声明,我是这个库的作者,它是开源且免费的(Apache 2.0 许可)