BufferedWriter 性能缓慢

Slow BufferedWriter Performance

我有一种方法可以将 44 MB 的数据从 ResultSet 写入 CSV 文件。但是,大约需要 3.5 分钟才能完成。对于只有 44 MB 的数据,这似乎很慢。任何人都可以看到任何减慢我的代码的东西吗?:

public static void convertToCSV(final ResultSet rs) throws SQLException, IOException {
    final BufferedWriter fw = new BufferedWriter(new FileWriter(new File("alert.csv")));
    while (rs.next()) {
        fw.write(rs.getString("FIELD1")+",");
        fw.write(rs.getString("FIELD2")+",");
        fw.write(rs.getString("FIELD3")+",");
        final String clobValue = rs.getString("FIELD4");
        if(clobValue==null)
            fw.write("null,");
        else{
            fw.write("\""+clobValue+"\",");
        }
        final Date date = new Date(rs.getLong("FIELD5"));
        final DateFormat format = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
        format.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
        final String dateTime[] = format.format(date).split(" ");
        fw.write(dateTime[0]+",");
        fw.write(dateTime[1]);

        fw.write("\n");
    }
    fw.close();
}

这可能取决于 JDBC 驱动程序、您的提取策略、磁盘...

但是你当然可以避免创建这么多临时对象:

  1. DateFormat 移出循环。
  2. 避免创建临时字符串 rs.getString("FIELD1")+"," 而是对编写器执行两次写入调用。
  3. 避免拆分操作。

下面应该会快一点

fw.write(rs.getString("FIELD1"));
fw.write(',');

还在循环​​之前声明和准备 SimpleDateFormat。

final DateFormat format = new SimpleDateFormat("yyyyMMdd','HH:mm:ss");

(此处不需要单引号。) 没有 split 这很昂贵。

这样的事情可能会更快

public static void convertToCSV(final ResultSet rs) throws SQLException, IOException {
    final BufferedWriter fw = new BufferedWriter(new FileWriter(new File("alert.csv")));
    final DateFormat format = new SimpleDateFormat("'yyyyMMdd','HH:mm:ss'");
    format.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));

    while (rs.next()) {
      StringBuilder sb = new StringBuilder();
      sb.append(rs.getString("FIELD1")).append(',')
        .append(rs.getString("FIELD2")).append(',')
        .append(rs.getString("FIELD3")).append(',');

        final String clobValue = rs.getString("FIELD4");
        if(clobValue==null)
          sb.append("null,");            
        else{
          sb.append('\"').append(clobValue).append('\"').append(',');
        }
        Date date = new Date(rs.getLong("FIELD5"));
        sb.append(format.format(date)).append('\n');
        fw.write(sb.toString());
    }
    fw.close();
}
  1. DateFormat 的创建只在循环外完成一次。
  2. 使用 StringBuilder
  3. 尽可能避免创建临时对象。

您甚至可以重用 StringBuilder,方法是将创建的内容移出循环并在编写后执行 setLength(0)

您可以增加生成 ResultSetStatement 的提取大小 - 这将减少返回数据库以提取下一批行所需的次数(默认设置为 10)。这样做的缺点是它会增加 ResultSet 的内存占用,因为它将在内存中保存更多数据。

您的 SimpleDatFormat 对象是在循环的每次迭代中使用相同的数据创建的 - 如果您将它移到循环之外,您只会实例化它一次。您也可以将 setTimeZone(...) 语句移到循环外。

String 每次使用变量值进行串联都会在内部创建一个新的 StringBuilder 对象 - 您可以通过确保从不在循环中进行串联来获得更多收益。您可以通过手动创建自己的 StringBuilder 来完成此操作,将整行的数据放入其中,然后将行写入 BufferedWriter 一次(可能会占用更多内存)或者您可以进行更多 write(...) 调用以避免进行连接。