【问题标题】:How to split a CSV file into multiple chunks and read those chunks in parallel in Java code如何将 CSV 文件拆分为多个块并在 Java 代码中并行读取这些块
【发布时间】:2012-06-21 08:51:00
【问题描述】:

我有一个非常大的 CSV 文件(1GB+),它有 100,000 行。

我需要编写一个 Java 程序来解析 CSV 文件中的每一行,以创建一个用于发送 HTTP 请求的正文。

换句话说,我需要发送 100,000 个与 CSV 文件中的行相对应的 HTTP 请求。如果我在一个线程中执行这些操作会很长。

我想创建 1,000 个线程来执行 i) 从 CSV 文件中读取一行,ii) 创建一个 HTTP 请求,其正文包含读取行的内容,以及 iii) 发送 HTTP 请求并接收响应。

这样,我需要将 CSV 文件分成 1000 个块,这些块之间应该没有重叠的行。

这种拆分过程的最佳方法是什么?

【问题讨论】:

  • 我有一个非常大的 CSV 文件(1GB+),它有 100,000 行,对于现在的计算机来说它一点也不大。如果您可以使所有 CPU 饱和,那么拥有比 CPU 多得多的线程是错误的。最后它会被绑定在 IO 部门,除非你故意尝试 DoS,否则向服务器发送大量并发请求也不是很明智。

标签: java csv


【解决方案1】:

如果您希望在同一操作中解压缩和解析,请查看https://github.com/skjolber/unzip-csv。

【讨论】:

    【解决方案2】:

    计划于本月发布的 Java 8 将得到改进的支持 为此,通过并行流和 lambdas。 Oracle 的tutorial 并行 流可能是一个很好的起点。

    请注意,这里的一个陷阱是过多的并行性。对于检索 URL 的示例,并行调用数量较少可能是个好主意。过多的并行性不仅会影响带宽和您要连接的网站,而且还会面临文件描述符不足的风险,在大多数运行 java 的环境中,文件描述符是严格受限的资源。

    Netflix 的RxJava 和Akka 是一些可能对您有所帮助的框架。请注意,这些框架并非易事,需要一些努力才能学习。

    【讨论】:

      【解决方案3】:

      通过构造 Runnable Task 的对象并将其传递给 Executors's submit() ,一旦获得该行,则在单线程中读取 CSV 文件将异步执行。

       public static void main(String[] args) throws IOException {
      
            String fName = "C:\\Amit\\abc.csv";
            String thisLine;
            FileInputStream fis = new FileInputStream(fName);
            DataInputStream myInput = new DataInputStream(fis);
            ExecutorService pool=Executors.newFixedThreadPool(1000);
            int count = 0;  // Concurrent request to Server barrier
      
            while ((thisLine = myInput.readLine()) != null) {
                if (count > 150) {
                    try {
                        Thread.sleep(100);
                        count = 0;
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
      
                pool.submit(new MyTask(thisLine));
                count++;
            }
      
          }
      }
      

      这里是你的任务:

      class MyTask implements Runnable {
            private String lLine;
            public MyTask(String line) {
                 this.lLine=line;
      
            }
      
            public void run() {
                // 1) Create Request  lLine
                // 2) send the HTTP request out and receive response
            }
      }
      

      【讨论】:

        【解决方案4】:

        您可以有一个线程来读取 CSV 的行并构建读取的行列表。当这达到某个限制时,例如100 行将其传递到固定大小的线程池以作为请求发送。

        我怀疑除非您的服务器有 1000 个内核,否则您可能会发现使用 10-100 个并发请求会更快。

        【讨论】:

        • 这取决于获得 HTTP 响应需要多长时间。如果涉及的服务器很慢,则大部分线程将等待 I/O。
        • 如果网络或服务器很慢,使用更大的批量或更小的请求可以改善加载时间。如果不进行测试,就不可能说出什么是最佳的。我的观点是;不要假设线程越多越好。
        • 这就是我的意思。由于您的应用程序更可能受 I/O 限制,因此基于内核数量的固定公式将不起作用,您必须尝试最有效的方法。 (或者编写一个自适应系统,这可能过于复杂了。)
        • 服务器和客户端在同一个内网,响应速度很快。
        • 我发现所有的答案都说单个线程读取 CSV 文件比多个线程好。我明白,1000 个线程会比 8 核计算机上的 10 个线程更糟糕。如果每个线程从第一行读取文件,肯定10比1慢。但是,是否可以在读取文件之前进行拆分操作将其拆分为相同大小的多个块?
        【解决方案5】:

        同时在多个位置读取单个文件不会让你走得更快(但它可能会大大减慢你的速度)。

        不是从多个线程读取文件,而是从单个线程读取文件,并并行处理这些行的处理。单个线程应该逐行读取您的 CSV,并将每一行放入队列中。然后,多个工作线程应该从队列中取出下一行,解析它,转换为请求,并根据需要并发处理请求。然后工作的拆分将由单个线程完成,确保没有丢失的行或重叠。

        【讨论】:

        • 是否可以在读取文件之前进行拆分操作将其拆分为相同大小的多个块?如果是这样,在文件被分割后,盯着多个线程并行读取块会比单个线程读取整个文件要快,不是吗?
        • @JuliaLi 不,不是真的:大文件通常占用磁盘上彼此靠近的多个块。由于无需重新定位磁头,磁盘访问连续块的速度要快得多,因此从磁盘读取大文件的速度要快得多。
        【解决方案6】:

        让一个线程逐行读取文件,并且对于读取的每一行,将任务发布到 ExecutorService 以执行每个任务的 HTTP 请求。

        从多个线程读取文件是行不通的,因为要读取nth 行,您必须先读取所有其他行。 (如果您的文件包含固定宽度的记录,理论上它可以工作,但 CSV 不是固定宽度格式。)

        【讨论】:

        • 你可以在知道列的情况下推断出行尾,这是可行的,但不值得付出努力。因此,如果有多个磁盘阵列和映射文件,则多个线程将起作用(对于读取部分)
        • 是否可以在读取文件之前进行拆分操作将其拆分为相同大小的多个块?如果是这样,在文件被分割后,盯着多个线程并行读取块。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-09-07
        • 1970-01-01
        • 2014-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多