2015-10-20 109 views
2

我有一个方法可以将44 MB的数据从ResultSet写入CSV文件。但是,大约需要3.5分钟才能完成。这似乎只有44 MB的数据缓慢。任何人都可以看到任何放缓我的代码?:Slow BufferedWriter性能

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(); 
} 
+1

增加获取ResultSet的语句上的获取大小可能会有所帮助 - 默认情况下,它会返回到数据库以每10行获取更多数据。权衡是如果您增加获取大小,您也会增加内存占用量。 – JonK

+1

它是否真的很慢,涉及到很多JDBC代码,您正在执行大量的String concats,这些String concats会生成大量需要gc'd的字符串。虽然日期格式不是线程安全的,但我只会在此方法内构建它并重用。并且可能使用'StringBuilder'来创建一个字符串并一次写入。 –

+0

如果您没有写入任何数据,您是否测量了时间并将SimpleDateFormat移出循环? –

回答

3

像这样的东西可能会更快

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. 尽可能避免创建临时对象。

你甚至可以通过移动创造出循环,并写它做setLength(0)后重用StringBuilder

3

这可能取决于JDBC驱动程序,你的抓取策略,盘...

但你肯定能避免产生这么多临时对象:

  1. DateFormat移出循环。
  2. 避免创建临时字符串rs.getString("FIELD1")+",",而是做作家两个写调用。
  3. 避免拆分操作。
2

以下应该会快一点。

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

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

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

(单引号这里不需要。) 没有split这是昂贵的。

+1

您也可以将'setTimeZone(...)'语句移到循环外部 – JonK

1

您可以增加产生ResultSetStatement上的提取大小 - 这将减少返回数据库以提取下一批记录的次数(默认设置为10) 。这种方法的缺点是,它会增加ResultSet的内存占用,因为它会在内存中举行更多的数据。

您的SimpleDatFormat对象在循环的每次迭代中都使用相同的数据创建 - 如果将它移动到循环外部,则只会将其初始化一次。您还可以将setTimeZone(...)声明外循环了。

String使用内部变量值的串联在内部创建一个新的StringBuilder对象 - 您可以通过确保您从不在循环中进行连接来获得进一步的收益。您可以通过手动创建自己的StringBuilder,将数据的整个行值了进去,然后写一行到BufferedWriter一次(在更多的内存的大概费用)做到这一点,或者你可以把更多的write(...)调用,以避免做串联。