性能问题-用java在unix box中编写巨大的csv文件时花费太多时间,请与MYSQL DB交互以对每条记录进行交叉检查

性能问题-用java在unix box中编写巨大的csv文件时花费太多时间,请与MYSQL DB交互以对每条记录进行交叉检查,java,mysql,performance,unix,jdbc,Java,Mysql,Performance,Unix,Jdbc,我的Java应用程序读取了大约6-7MB的巨大csv文件大小,有50k到60k条记录,中间连接到mysql数据库,对每条记录进行交叉检查,只需选择查询并执行一些操作,将所有记录写入tmp csv文件。但这里的问题是,这个过程大约需要6-7个小时来编写tmp文件 示例代码- public static void updateTransactionCsvFiles(String inputFilePath , String existingFileName,File outputFolder,Fil

我的Java应用程序读取了大约6-7MB的巨大csv文件大小,有50k到60k条记录,中间连接到mysql数据库,对每条记录进行交叉检查,只需选择查询并执行一些操作,将所有记录写入tmp csv文件。但这里的问题是,这个过程大约需要6-7个小时来编写tmp文件

示例代码-

public static void updateTransactionCsvFiles(String inputFilePath , String existingFileName,File outputFolder,File archiveFolder,String tpName) throws IOException {

     File inputFile = new File(inputFilePath);
     Charset charset = Charset.forName("UTF-8");
     BufferedReader in = new BufferedReader(new InputStreamReader(new   FileInputStream(inputFile), charset));
     CSVReader reader = new CSVReader(in, '|','"');
     List<String[]> csvBody = reader.readAll();

    File newFile = new File("tmp.csv");
               BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile), charset),32768);
               CSVWriter writer = new CSVWriter(bw, '|',CSVWriter.NO_QUOTE_CHARACTER);

    Connection connection = null;
    try {
        Class.forName("oracle.jdbc.driver.OracleDriver"); // JDBC Type4
       connection =  DriverManager.getConnection("jdbc:mysql://"+dbhostname+"/db",dbusername,dbpassword);
    }catch(Exception e){
                e.printStackTrace();
            }
    for(int row=0 ; row < csvBody.listSize; row++){
        String eachRecord[]= csvBody.get(row);
         String array[]= csvBody.get(row);
         array = Arrays.copyOf(array, array.length + 1); //create new array from old array and allocate three more element
        array[array.length - 1] = "brandName";
         csvBody.remove(row);
         csvBody.add(row, array);


        String customerId = eachRecord[3];
        List tpList = new ArrayList();
        tpList.add("100");
        StringBuilder builder = new StringBuilder();
                for( int i = 0 ; i < tpList.size(); i++ ) {
                    builder.append("?,");
                }           

      String query = "select customer_id from customer where client_id IN " + 
      "(" 
                             + builder.deleteCharAt( builder.length() -1 ).toString() + ")"+" and customer_id = ? "; 
       PreparedStatement pstmt =connection.prepareStatement(query); 
           pstmt.setObject(index,customerId);
            rs = pstmt.executeQuery();
           String pid ="";      
           while(rs.next()){
                    pid=rs.getInt(3);
                }

             csvBody.get(row)[4] = pid;            


            pstmt =con.prepareStatement(""SELECT status, senttime, process_id FROM feed WHERE customer_id = ? and sent_time =(select MAX(senttime) FROM feeds WHERE customer_id = ? ) "");
       pstmt.setObject(1,customerId);
        pstmt.setObject(2,customerId);
      rs = pstmt.executeQuery();
    while(rs.next()){
            feedstatus = rs.getString(1);
            senttime = rs.getTimestamp(2);
            processid =rs.getInt(3);
        }
               csvBody.get(row)[6] = feedstatus;  
                rs.close(); 
                pstmt.close();
      }

        writer.writeAll(csvBody); // write all records in to the file.
        writer.flush();
        writer.close();
        csvBody.clear();
        csvBody = null;
        reader.close();
    }   

我建议进行以下更改:

不需要一次读取文件的所有行并进行处理。相反,您可以逐行读取它,执行查询并将其写入文件。这正是BufferedReader应该使用的方式。 您正在使用csvBody.removeow;和csvBody.addrow,数组;这不是一个好的做法。我们不应该修改正在迭代的集合。 您不需要为每次迭代创建新的PreparedStatement对象。您可以在for循环之外声明它,并通过设置参数在每次迭代中执行它。 csvBody.clear;不需要,因为我们不会阅读所有的行。 如果还没有索引,您可能需要在client_id列上添加索引。 下面是一个如何逐行读取巨大csv文件的示例:

BufferedReader in = new BufferedReader(new InputStreamReader(new   FileInputStream(inputFile), charset));
CSVReader reader = new CSVReader(in, '|','"');

String [] nextLine;
while ((nextLine = reader.readNext()) != null) {
//Process line
}

我注意到,当这个代码部署在QA环境中时,这个过程需要15-20分钟才能完成。但是,虽然在生产环境中部署相同的代码,但相同的过程需要6-7个小时,因为prod拥有大量的客户数据:使用2-3个select查询执行某些验证时,DBmysql交互时的最大时间消耗似乎是最大的;在prod env上花费了太多的时间。我是否应该使用hibernate而不是jdbc来缓存这些多查询select语句的输出,这样它就不会每次都命中数据库来获取结果?还是应该使用可调用stmt的存储过程?请建议是否需要实施任何其他解决方案以减少处理时间。或者我是否需要进行查询优化?如果需要,则如何进行我已从for循环中取出所有PreparedStatement查询,并在循环之前初始化所有PreparedStatement,但没有看到任何性能改进:您需要添加逐行处理文件的循环,而不是将所有内容都读入列表。这应该会显著提高性能。我也尝试过逐行循环处理文件,但现在性能降低了,甚至时间增加了都没有改善:应用程序与prod数据库交互时,似乎消耗了最长的时间,因为它有大量数据。您正在查询客户端id,该表在该列上有索引吗?如果没有,您可以尝试添加一个吗?