2017-01-07 1 views
1

J'essaie d'analyser les valeurs d'un fichier CSV vers une base de données SQLite, mais le fichier est assez volumineux (~ 2 500 000 lignes). J'ai couru mon programme pendant quelques heures, imprimant où il était, mais selon mes calculs, le fichier aurait pris environ 100 heures à analyser complètement, donc je l'ai arrêté.Ajout efficace d'énormes quantités de données à partir de fichiers CSV dans une base de données SQLite en Java

Je vais devoir exécuter ce programme en tant que processus d'arrière-plan au moins une fois par semaine, sur un nouveau fichier CSV qui est environ 90% similaire au précédent. J'ai trouvé quelques solutions pour améliorer mon programme. Cependant, je ne sais pas grand-chose sur les bases de données, alors j'ai des questions sur chacune de mes solutions.

  • Existe-t-il un moyen plus efficace de lire un fichier CSV que ce que j'ai déjà?

  • Instanciation d'un objet ObjectOutputStream, et stockage en tant que BLOB de façon coûteuse? Je pourrais directement ajouter les valeurs à la place, mais j'utilise le BLOB plus tard, le stocker maintenant m'évite d'en instancier un nouveau plusieurs fois.

  • Le regroupement de connexions ou la modification de la manière dont j'utilise la connexion d'une autre manière sont-ils plus efficaces? Je définis la colonne URL comme UNIQUE afin que je puisse utiliser INSERT OR IGNORE, mais le test sur des jeux de données plus petits (~ 10000 lignes) indique qu'il n'y a pas de gain de performances par rapport à la suppression de la table et au repeuplement. Existe-t-il un moyen plus rapide d'ajouter uniquement des valeurs uniques?

  • Y a-t-il des erreurs évidentes que je commets? (Encore une fois, je sais très peu de choses sur les bases de données)

    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

Idéalement, vous ne voulez pas créer une nouvelle instruction préparée avec chaque ligne du fichier. Vous voulez le réutiliser. – 4castle

+0

Il semble que votre code fonctionne actuellement, et vous cherchez à l'améliorer. Généralement, ces questions sont trop controversées pour ce site, mais vous pourriez trouver plus de chance à [CodeReview.SE] (// codereview.stackexchange.com/tour). N'oubliez pas de lire [leurs exigences] (// codereview.stackexchange.com/help/on-topic) car ils sont un peu plus strictes que ce site. – 4castle

+0

@ 4castle Merci. J'ai déplacé le PreparedStatement hors de la boucle, et l'ai testé sur 1000 lignes et gagné environ 3 secondes d'amélioration. Donc c'est un début. – Sam

Répondre

1

Le plus grand bottleck dans votre code est que vous n'êtes pas les Batching opérations d'insertion. Vous devriez vraiment appeler pstmt.addBatch(); au lieu de pstmt.executeUpdate(); et exécuter le lot une fois que vous avez quelque chose comme un lot de 10K lignes à insérer. Sur le côté de l'analyse syntaxique CSV, vous devriez vraiment envisager d'utiliser une bibliothèque csv pour faire l'analyse pour vous. Univocity-parsers a l'analyseur CSV le plus rapide et il devrait traiter ces 2,5 millions de lignes en moins d'une seconde. Je suis l'auteur de cette bibliothèque en passant.

String.split() est pratique mais pas rapide. Pour quelque chose de plus que quelques dizaines de lignes, cela n'a pas de sens d'utiliser cela.

Espérons que cela aide.