Откройте CSV Performance для записи данных - PullRequest
0 голосов
/ 20 марта 2020

Я пришел по ссылке: https://github.com/hyee/OpenCSV, которая значительно улучшает время записи JDB C ResultSet в CSV благодаря setAsyncMode, RESULT_FETCH_SIZE

//Extract ResultSet to CSV file, auto-compress if the fileName extension is ".zip" or ".gz"
//Returns number of records extracted
public int ResultSet2CSV(final ResultSet rs, final String fileName, final String header, final boolean aync) throws Exception {
    try (CSVWriter writer = new CSVWriter(fileName)) {
        //Define fetch size(default as 30000 rows), higher to be faster performance but takes more memory
        ResultSetHelperService.RESULT_FETCH_SIZE=10000;
        //Define MAX extract rows, -1 means unlimited.
        ResultSetHelperService.MAX_FETCH_ROWS=20000;
        writer.setAsyncMode(aync);
        int result = writer.writeAll(rs, true);
        return result - 1;
    }
}

Но проблема это я не знаю, как я могу слиться выше в моем требовании. Так как в ссылке есть много других классов, которые я не уверен, что они делают, и если мне даже нужно это для моего требования. Тем не менее, я пытался, но он не компилируется всякий раз, когда я включаю код с двумя комментариями строки. Ниже мой код.

Буду очень признателен за любую помощь в том, как мне этого добиться.

package test;



import java.io.BufferedWriter;
import java.io.FileWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Date;


import com.opencsv.CSVWriter;
import com.opencsv.ResultSetHelperService;

public class OpenCSVTest1
{

    static Connection con =null;
    static Statement stmt = null;
    static ResultSet rs = null;

    public static void main(String args[]) throws Exception
    { 


        connection ();
        retrieveData(con);

    }

    private static void connection() throws Exception 
    {


        try
        {
            Class.forName("<jdbcdriver>");
            con = DriverManager.getConnection("jdbc:","<username>","<pass>");
            System.out.println("Connection successful");
        }


        catch (Exception e)
        {
            System.out.println("Exception while establishing sql connection");
            throw e;
        }
    }


    private static void retrieveData(Connection con) throws Exception
    {
        try
        {
            stmt=con.createStatement(); 
            stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
            String query = "SELECT  * FROM dbo.tablename";

            rs=stmt.executeQuery(query);

            CSVWriter writer = new CSVWriter(new BufferedWriter(new FileWriter("C:\\Data\\File1.csv")));    

            ResultSetHelperService service = new ResultSetHelperService(); 

            /***    ResultSetHelperService.RESULT_FETCH_SIZE=10000;   ***/    // to add 


            service.setDateTimeFormat("yyyy-MM-dd HH:mm:ss.SSS"); 

            System.out.println("**** Started writing Data to CSV **** " +  new Date());         

            writer.setResultService(service);

            /***   writer.setAsyncMode(aync);  ***/   // to add 


            int lines = writer.writeAll(rs, true, true, false); 

            writer.flush();
            writer.close();

            System.out.println("** OpenCSV -Completed writing the resultSet at " +  new Date() + " Number of lines written to the file " + lines);  
        }


        catch (Exception e)
        {
            System.out.println("Exception while retrieving data" );
            e.printStackTrace();
            throw e;
        }

        finally 
        {
            rs.close();
            stmt.close();
            con.close();

        }
    }







}

ОБНОВЛЕНИЕ

Я обновил свой код. Прямо сейчас код записывает полный набор результатов в CSV за раз, используя метод writeAll, что приводит к потере времени.

Теперь я хочу записать набор результатов в CSV в пакетах, поскольку первый столбец набора результатов всегда будет иметь динамически генерируется с помощью запроса SELECT для столбца автоинкремента (Sqno) со значениями как (1,2,3 ..). Поэтому не уверен, как я могу прочитать наборы результатов в первом столбце и разделить его соответствующим образом для записи в CSV. может быть, HashMap может помочь, поэтому я также добавил код преобразования resultset-tohashmap, если требуется.

import com.opencsv.CSVWriter;
import com.opencsv.ResultSetHelperService;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class OpenCSVTest1
{ 
    static int fetchlimit_src  = 100;
    static Connection con =null;
    static Statement stmt = null;
    static ResultSet rs = null;
    static String filename = "C:\\Data\\filename.csv";
    static CSVWriter writer;
    public static void main(String args[])
    {  
        try
        {  
            connection();
            retrieveData(con);
        }
        catch(Exception e)
        { 
            System.out.println(e);
        }  
    }
    private static void connection() throws Exception 
    {
        try
        {
            Class.forName("<jdbcdriver>");
            con = DriverManager.getConnection("jdbc:","<username>","<pass>");
            System.out.println("Connection successful");
        }
        catch (Exception e)
        {
            System.out.println("Exception while establishing sql connection");
            throw e;
        }
    }  
    private static void retrieveData(Connection con) throws Exception
    {
        try
        {
            stmt=con.createStatement(); 
            String query = "SELECT ROWNUM AS Sqno, * FROM dbo.tablename ";   // Oracle
            //  String query = "SELECT ROW_NUMBER() OVER(ORDER BY Id ASC) AS Sqno, *  FROM dbo.tablename ";  // SQLServer
            System.out.println(query);
            stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
            stmt.setFetchSize(fetchlimit_src);
            System.out.println("**** Started querying src **** " +  new Date());
            rs=stmt.executeQuery(query);
            System.out.println("**** Completing querying src **** " +  new Date());
            //  resultset_List(rs);   // If required store resultset(rs) to HashMap
            writetoCSV(rs,filename);  
            /** How to write resultset to CSV in batches instead of writing all at once to speed up write performance ? 
             * Hint: resultset first column is Autoincrement [Sqno] (1,2,3...) which might help to split result in batches.
             *
             **/
        }
        catch (Exception e)
        {
            System.out.println("Exception while retrieving data" );
            e.printStackTrace();
            throw e;
        }
        finally 
        {
            rs.close();
            stmt.close();
            con.close();
        }
    }
    private static List<Map<String, Object>> resultset_List(ResultSet rs) throws SQLException
    {
        ResultSetMetaData md = rs.getMetaData();
        int columns = md.getColumnCount();
        List<Map<String, Object>> rows = new ArrayList<Map<String, Object>>();
        while (rs.next())
        {
            Map<String, Object> row = new HashMap<String, Object>(columns);
            for(int i = 1; i <= columns; ++i)
            {
                row.put(md.getColumnName(i), rs.getObject(i));
            }
            rows.add(row);
        }
        //    System.out.println(rows.toString());
        return rows;
    }
    private static void writetoCSV(ResultSet rs, String filename) throws Exception
    {
        try
        {
            writer = new CSVWriter(new BufferedWriter(new FileWriter(filename)));
            ResultSetHelperService service = new ResultSetHelperService();
            service.setDateTimeFormat("yyyy-MM-dd HH:mm:ss.SSS");
            long batchlimit = 1000;
            long Sqno  = 1;
            ResultSetMetaData rsmd = rs.getMetaData();
            String columnname = rsmd.getColumnLabel(1);  // To retrieve columns with labels (for example SELECT ROWNUM AS Sqno)
            System.out.println("**** Started writing Data to CSV **** " +  new Date());
            writer.setResultService(service);
            int lines = writer.writeAll(rs, true, true, false); 
    System.out.println("** OpenCSV -Completed writing the resultSet at " +  new Date() + " Number of lines written to the file " + lines);
        }
        catch (Exception e)
        {
            System.out.println("Exception while writing data" );
            e.printStackTrace();
            throw e;
        }
        finally
        {
            writer.flush();
            writer.close();
        }
    }
}  

1 Ответ

0 голосов
/ 23 марта 2020

Вы должны быть в состоянии использовать образец OpenCSV, в точности так, как это указано в документации. Таким образом, вам не нужно писать какие-либо собственные журналы дозирования c.

. Мне удалось записать 6 миллионов записей в CSV-файл примерно за 10 секунд. Чтобы было ясно - это было только время записи файла, а не время извлечения данных из БД, но я думаю, что оно должно быть достаточно быстрым для ваших нужд.

Вот ваш код с адаптациями для использования на основе OpenCSV о его документированном подходе ... Но, пожалуйста, смотрите предупреждение в конце моих заметок!

import com.opencsv.CSVWriter;
import com.opencsv.ResultSetHelperService;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Date;
import java.text.SimpleDateFormat;

public class OpenCSVDemo {

    static int fetchlimit_src = 100;
    static Connection con = null;
    static Statement stmt = null;
    static ResultSet rs = null;
    static String filename = "C:\\Data\\filename.csv";

    public static void main(String args[]) {
        try {
            connection();
            retrieveData(con);

        } catch (Exception e) {
            System.out.println(e);
        }
    }

    private static void connection() throws Exception {
        try {
            final String jdbcDriver = "YOURS GOES HERE";
            final String dbUrl = "YOURS GOES HERE";
            final String user = "YOURS GOES HERE";
            final String pass = "YOURS GOES HERE";
            Class.forName(jdbcDriver);
            con = DriverManager.getConnection(dbUrl, user, pass);
            System.out.println("Connection successful");
        } catch (Exception e) {
            System.out.println("Exception while establishing sql connection");
            throw e;
        }
    }

    private static void retrieveData(Connection con) throws Exception {
        try {
            stmt = con.createStatement();
            String query = "select title_id, primary_title from imdb.title";
            System.out.println(query);
            stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
            stmt.setFetchSize(fetchlimit_src);
            System.out.println("**** Started querying src **** " + new Date());
            rs = stmt.executeQuery(query);
            System.out.println("**** Completing querying src **** " + new Date());
            //  resultset_List(rs);   // If required store resultset(rs) to HashMap

            System.out.println();
            String timeStamp = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss").format(new Date());
            System.out.println("Started writing CSV:  " + timeStamp);
            writeToCsv(rs, filename, null, Boolean.FALSE);
            timeStamp = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss").format(new Date());
            System.out.println("Finished writing CSV: " + timeStamp);
            System.out.println();

        } catch (Exception e) {
            System.out.println("Exception while retrieving data");
            e.printStackTrace();
            throw e;
        } finally {
            rs.close();
            stmt.close();
            con.close();
        }
    }

    public static int writeToCsv(final ResultSet rs, final String fileName, 
            final String header, final boolean aync) throws Exception {
        try (CSVWriter writer = new CSVWriter(fileName)) {
            //Define fetch size(default as 30000 rows), higher to be faster performance but takes more memory
            ResultSetHelperService.RESULT_FETCH_SIZE = 1000;
            //Define MAX extract rows, -1 means unlimited.
            ResultSetHelperService.MAX_FETCH_ROWS = 2000;
            writer.setAsyncMode(aync);
            int result = writer.writeAll(rs, true);
            return result - 1;
        }
    }

}

Примечания:

1) Я использовал "asyn c", установленный на false:

writeToCsv(rs, filename, null, Boolean.FALSE);

Возможно, вы захотите поэкспериментировать с этими и другими настройками, чтобы увидеть, имеют ли они какое-либо существенное значение для вас.

2) Относительно вашего комментария " ссылка включает в себя много других классов": весь JAR-файл библиотеки OpenCSV должен быть включен в ваш проект, также как и связанный JAR-нарушитель:

opencsv.jar
disruptor-3.3.6.jar

Чтобы получить файлы JAR, go для GitHub page , нажмите зеленую кнопку, выберите загрузку zip, распакуйте zip-файл и посмотрите в папку «OpenCSV-master \ release».

Добавьте эти два JAR-файла в Ваш проект обычным способом (зависит от того, как вы строите ваш проект).

3) ВНИМАНИЕ: Этот код работает нормально при использовании Oracle Java 8 JDK / JRE. Если вы попытаетесь использовать OpenJDK (например, для Java 13 или аналогичный), он не будет работать. Это из-за некоторых изменений за кулисами скрытых классов. Если вам интересно, здесь есть более подробная информация здесь .

Если вам нужно использовать OpenJDK-версию Java, вам, возможно, повезет больше с библиотекой, в которой эта библиотека CSV основан: см. здесь .

...