2017-01-07 30 views
1

CSVファイルからSQLite DBに値を解析しようとしていますが、ファイルはかなり大きいです(2,500,000行)。私はプログラムを数時間走らせて、どこまで印刷したのですが、私の計算では、ファイルは完全に解析するのに約100時間かかってしまいました。CSVファイルから大量のデータをJavaのSQLite DBに効率的に追加する

私は、このプログラムをバックグラウンドプロセスとして、少なくとも1週間に1回、前回とほぼ同じ90%の新しいCSVファイルで実行する必要があります。私は自分のプログラムを改善するためのいくつかの解決策を思いついた。しかし、私はデータベースについてよく知らないので、それぞれのソリューションについて質問があります。

  • 私がすでに持っているものよりも効率的にCSVファイルを読む方法はありますか?

  • ObjectOutputStreamをインスタンス化し、それをBLOBとして非常に計算コストがかかりますか?代わりに値を直接追加することもできますが、BLOBを後で使用するので、新しい値を複数回インスタンス化できなくなります。

  • 接続プーリング、または他の方法でConnectionを使用する方法を変更する方が効率的ですか?

  • 私はINSERT OR IGNOREを使用することができるようにURL列を設定していますが、これを小さなデータセット(〜10000行)でテストすると、テーブルを削除して再投入する場合に比べてパフォーマンスが向上しません。一意の値だけを追加する方が速いのですか?

  • 私が作っている間違いはありますか?あなたのコードで最大のbottleckは、あなたが挿入操作をバッチ処理されていないことである

    public class Database{ 
    
    public void createResultsTable(){ 
        Statement stmt; 
        String sql = "CREATE TABLE results(" 
          + "ID  INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT, " 
          + "TITLE TEXT  NOT NULL, " 
          + "URL  TEXT  NOT NULL UNIQUE, " 
          ... 
          ... 
          + "SELLER TEXT  NOT NULL, " 
          + "BEAN  BLOB);"; 
        try { 
         stmt = c.createStatement(); 
         stmt.executeUpdate(sql); 
        } catch (SQLException e) { e.printStackTrace();} 
    
    
    } 
    
    
    public void addCSVToDatabase(Connection conn, String src){ 
    
        BufferedReader reader = null; 
        DBEntryBean b; 
        String[] vals; 
    
        try{ 
         reader = new BufferedReader(new InputStreamReader(new FileInputStream(src), "UTF-8")); 
         for(String line; (line = reader.readLine()) != null;){ 
          //Each line takes the form: "title|URL|...|...|SELLER" 
          vals = line.split("|"); 
    
          b = new DBEntryBean(); 
          b.setTitle(vals[0]); 
          b.setURL(vals[1]); 
          ... 
          ... 
          b.setSeller(vals[n]); 
    
          insert(conn, b); 
         } 
        } catch(){ 
    
        } 
    } 
    
    
    public void insert(Connection conn, DBEntryBean b){ 
    
        PreparedStatement pstmt = null; 
        String sql = "INSERT OR IGNORE INTO results(" 
          + "TITLE, " 
          + "URL, " 
          ... 
          ... 
          + "SELLER, " 
          + "BEAN" 
          + ");"; 
    
        try { 
         pstmt = c.prepareStatement(sql); 
         pstmt.setString(Constants.DB_COL_TITLE, b.getTitle());  
         pstmt.setString(Constants.DB_COL_URL, b.getURL());  
         ... 
         ... 
         pstmt.setString(Constants.DB_COL_SELLER, b.getSeller()); 
    
         // ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
         // oos = new ObjectOutputStream(baos); 
         // oos.writeObject(b); 
         // byte[] bytes = baos.toByteArray(); 
         // pstmt.setBytes(Constants.DB_COL_BEAN, bytes); 
         pstmt.executeUpdate(); 
    
        } catch (SQLException e) { e.printStackTrace(); 
        } finally{ 
         if(pstmt != null){ 
          try{ pstmt.close(); } 
          catch (SQLException e) { e.printStackTrace(); } 
         } 
    
        } 
    } 
    
    
    } 
    
+0

ファイルの各行で新しいprepared statementを作成したくないのが理想的です。あなたはそれを再利用したい。 – 4castle

+0

あなたのコードは現在動作しているようですが、あなたはそれを改善しようとしています。一般的に、これらの質問はこのサイトでは強すぎますが、[CodeReview.SE](// codereview.stackexchange.com/tour)のほうが良いかもしれません。このサイトよりも少し厳密であるため、[必要条件](// codereview.stackexchange.com/help/on-topic)を必ずお読みください。 – 4castle

+0

@ 4castleありがとうございます。 PreparedStatementをループから外して1000行でテストし、約3秒の改善が得られました。だからそれはスタートです。 – Sam

答えて

1

(繰り返しますが、私は、データベースについてほとんど知っています)。 pstmt.executeUpdate();の代わりにpstmt.addBatch();に電話し、挿入する10K行のバッチがあればバッチを実行する必要があります。

CSV解析側では、実際にCSVライブラリを使用して解析することを検討する必要があります。 Univocity-parsersは最速のCSVパーサを持ち、250万行を1秒未満で処理する必要があります。私は途中でこの図書館の著者です。

String.split()は便利ですが高速ではありません。数十列以上のものについては、これを使うのは意味がありません。

これが役に立ちます。

関連する問題