惰性 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 许可)