提高在java中将查询结果写入CSV的性能

时间:2009-07-14 18:15:54

标签: java performance

我有以下代码执行查询并将其直接写入字符串缓冲区,然后将其转储到CSV文件中。我需要写大量的记录(最多一百万)。这适用于一百万条记录,对于一个大约200mb的文件大约需要半小时!在我看来好像很多时间,不确定这是否是最好的。即使它包括使用其他jar / db连接工具,也请向我推荐更好的方法。

....
eventNamePrepared = con.prepareStatement(gettingStats + 
    filterOptionsRowNum + filterOptions);
ResultSet rs = eventNamePrepared.executeQuery(); 
int i=0;
try{
......
FileWriter fstream = new FileWriter(realPath + 
    "performanceCollectorDumpAll.csv");
BufferedWriter out = new BufferedWriter(fstream);
StringBuffer partialCSV = new StringBuffer();


while (rs.next()) { 
  i++;
  if (current_appl_id_col_display) 
      partialCSV.append(rs.getString("current_appl_id") + ",");
  if (event_name_col_display) 
      partialCSV.append(rs.getString("event_name") + ",");
  if (generic_method_name_col_display) 
      partialCSV.append(rs.getString("generic_method_name") + ",");
  ..... // 23 more columns to be copied same way to buffer
  partialCSV.append(" \r\n");
  // Writing to file after 10000 records to prevent partialCSV 
  // from going too big and consuming lots of memory
  if (i % 10000 == 0){
      out.append(partialCSV);
      partialCSV = new StringBuffer();
  }
}               
con.close();
out.append(partialCSV);
out.close();

谢谢,

7 个答案:

答案 0 :(得分:6)

只需直接写入BufferedWriter,而不是构建StringBuffer

另请注意,您应该使用StringBuilder代替StringBuffer ... StringBuffer有内部锁定,这通常不是必需的。

答案 1 :(得分:5)

分析通常是了解为什么某些东西变慢的唯一确定方法。但是,在这个例子中,我会建议两件不为人知的事情:

  1. 直接写入缓冲的编写器,而不是使用StringBuilder创建自己的缓冲。
  2. 通过整数序数引用结果集中的列。解析列名时,某些驱动程序可能会很慢。

答案 2 :(得分:3)

你可以调整各种各样的东西,但是为了真正的改进,我会尝试使用你用来生成文件的任何数据库的本机工具。如果它是SQL Server,这将是bcp,它可以获取查询字符串并直接生成文件。如果你需要从Java调用它,你可以将它作为一个进程生成。

作为一个例子,我刚刚运行了这个......

bcp“select * from trading..bar_db”queryout bar_db.txt -c -t,-Uuser -Ppassword -Sserver

...这会在10秒内生成一个包含200万行的170MB文件。

答案 3 :(得分:2)

我只是想为 Jared Oberhaus 的建议添加一个示例代码:

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class CSVExport {
    public static void main(String[] args) throws Exception {
    String table = "CUSTOMER";
    int batch = 100;

    Class.forName("oracle.jdbc.driver.OracleDriver");
    Connection conn = DriverManager.getConnection(
        "jdbc:oracle:thin:@server:orcl", "user", "pass");
    PreparedStatement pstmt = conn.prepareStatement(
        "SELECT /*+FIRST_ROWS(" + batch + ") */ * FROM " + table);
    ResultSet rs = pstmt.executeQuery();
    rs.setFetchSize(batch);
    ResultSetMetaData rsm = rs.getMetaData();
    File output = new File("result.csv");
    PrintWriter out = new PrintWriter(new BufferedWriter(
        new OutputStreamWriter(
        new FileOutputStream(output), "UTF-8")), false);
    Set<String> columns = new HashSet<String>(
        Arrays.asList("COL1", "COL3", "COL5")
    );
    while (rs.next()) {
        int k = 0;
        for (int i = 1; i <= rsm.getColumnCount(); i++) {
        if (columns.contains(rsm.getColumnName(i).toUpperCase())) {
            if (k > 0) {
                out.print(",");
            }
            String s = rs.getString(i);
            out.print("\"");
            out.print(s != null ? s.replaceAll("\"", "\\\"") : "");
            out.print("\"");
            k++;
        }
        }
        out.println();
    }
    out.flush();
    out.close();
    rs.close();
    pstmt.close();
    conn.close();
    }
}

答案 4 :(得分:0)

我有两个快速的想法。首先,你确定写入磁盘是问题吗?你真的可以花大部分时间等待来自数据库的数据吗?

第二种是尝试删除所有+“,”s,然后使用更多.adnds。它可能有助于考虑你做这些的频率。

答案 5 :(得分:0)

您提到您正在使用Oracle。您可能希望使用Oracle外部表功能或Oracle数据泵进行调查,具体取决于您要执行的操作。

请参阅http://www.orafaq.com/node/848(将数据卸载到外部文件中......)

另一个选项可能是通过sqlplus连接并在查询之前运行“spool”。

答案 6 :(得分:0)

写入缓冲的写入器通常很快“足够”。如果它不适合你,那么其他东西正在减慢它。

分析它的最简单方法是使用最新JDK中提供的jvisualvm。