21xrx.com
2025-03-23 16:33:37 Sunday
文章检索 我的文章 写文章
Java实现千万级数据导出CSV文件
2023-06-16 13:25:07 深夜i     49     0
Java 千万级数据 CSV文件 流式写入 分批次写入 多线程写入

CSV(Comma-Separated Values)是一种常见的数据存储格式,它以逗号作为字段分隔符,以换行符作为行分隔符。在数据集很大的情况下,导出CSV文件可能会面临内存溢出等问题。本文将介绍如何使用Java实现千万级数据导出CSV文件的方法。

1. 使用流式写入文件

在处理大量数据时,使用流式写入文件是一个比较好的选择。这种方法可以避免一次性将所有数据加载到内存中,而是使用缓存区一行一行地写入数据。以下代码演示了使用流式写入文件的具体实现过程:

public static void writeToCSV(List dataList, String filePath) {
  try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
    for (Data data : dataList) {
      writer.append(data.getId())
         .append(",")
         .append(data.getName())
         .append(",")
         .append(data.getAge())
         .append("\n");
    }
  } catch (IOException e) {
    e.printStackTrace();
  }
}

以上代码中,文件写入过程通过BufferedWriter实现,每写入一行数据,就刷新缓存区。这种方式可以在写入数据时不产生内存溢出的情况下导出CSV文件。

2. 分批次写入文件

除了使用流式写入文件的方式外,另一种常见的处理大量数据的方法是分批次写入文件。对于数据量较大的情况,可以将数据分为多个批次,每个批次只写入几万条数据。这样可以减轻单次写入数据的负担,避免一次性将大量数据写入文件中。

以下代码演示了分批次写入文件的具体实现过程:

public static void writeToCSV(List dataList, String filePath, int batchSize) {
  int totalCount = dataList.size();
  int batchCount = (totalCount % batchSize == 0) ? totalCount / batchSize : (totalCount / batchSize + 1);
  try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
    for (int i = 0; i < batchCount; i++) {
      int startIndex = i * batchSize;
      int endIndex = Math.min((i + 1) * batchSize, totalCount);
      List subList = dataList.subList(startIndex, endIndex);
      for (Data data : subList) {
        writer.append(data.getId())
           .append(",")
           .append(data.getName())
           .append(",")
           .append(data.getAge())
           .append("\n");
      }
      writer.flush();
    }
  } catch (IOException e) {
    e.printStackTrace();
  }
}

以上代码中,数据按照batchSize进行分批处理,每个批次均在同一个文件中进行写入。当批次数量很多时,可以考虑将每个批次写入到不同的文件中,避免一个文件过大。

3. 使用多线程方式写入文件

在数据量较大的情况下,单线程写入数据可能会导致效率较低。使用多线程方式写入数据可以大幅提升导出CSV文件的效率。以下代码演示了如何使用多线程方式写入文件:

public static void writeToCSV(List dataList, String filePath, int threadCount) {
  int totalCount = dataList.size();
  try {
    ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
    List
  
   > tasks = new ArrayList<>();
  
 
    for (int i = 0; i < threadCount; i++) {
      final int batchIndex = i;
      tasks.add(new Callable
  () {
 
        @Override
        public Void call() throws Exception {
          try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath + "_" + batchIndex))) {
            for (int i = batchIndex; i < totalCount; i += threadCount) {
              Data data = dataList.get(i);
              writer.append(data.getId())
                 .append(",")
                 .append(data.getName())
                 .append(",")
                 .append(data.getAge())
                 .append("\n");
            }
          }
          return null;
        }
      });
    }
    executorService.invokeAll(tasks);
    executorService.shutdown();
  } catch (InterruptedException | IOException e) {
    e.printStackTrace();
  }
}

以上代码中,使用ExecutorService创建了多个线程,每个线程将数据按照线程数进行分组,然后写入到不同的文件中。这样可以避免线程间竞争同一个文件写入数据的问题,同时也可以大幅提高导出CSV文件的效率。

  
  

评论区