2016-01-12 20 views
6

Ho letto il file e creato un oggetto da esso e archiviato nel database postgresql. Il mio file ha 100.000 documenti che ho letto da un file, li ho divisi e infine li ho archiviati nel database. Non riesco a creare List<> e memorizzare tutti i documenti in List<> perché la mia RAM è piccola. Il mio codice per leggere e scrivere nel database sono i seguenti. Ma My JVM Heap si riempie e non può continuare a memorizzare più documenti. Come leggere file e archiviarli nel database in modo efficiente.Come inserire i dati il ​​più velocemente possibile con Hibernate

public void readFile() { 
    StringBuilder wholeDocument = new StringBuilder(); 
    try { 
     bufferedReader = new BufferedReader(new FileReader(files)); 
     String line; 
     int count = 0; 
     while ((line = bufferedReader.readLine()) != null) { 
      if (line.contains("<page>")) { 
       wholeDocument.append(line); 
       while ((line = bufferedReader.readLine()) != null) { 
        wholeDocument = wholeDocument.append("\n" + line); 
        if (line.contains("</page>")) { 
         System.out.println(count++); 
         addBodyToDatabase(wholeDocument.toString()); 

         wholeDocument.setLength(0); 
         break; 
        } 
       } 
      } 
     } 
     wikiParser.commit(); 
    } catch (FileNotFoundException e) { 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } finally { 
     try { 
      bufferedReader.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

public void addBodyToDatabase(String wholeContent) { 
    Page page = new Page(new Timestamp(System.currentTimeMillis()), 
      wholeContent); 
    database.addPageToDatabase(page); 
} 

public static int counter = 1; 

public void addPageToDatabase(Page page) { 
    session.save(page); 
    if (counter % 3000 == 0) { 
     commit(); 
    } 
    counter++; 
} 
+0

Forse avete bisogno di aggiungere 'StringBuilder wholeDocument = new StringBuilder();' da qualche parte dentro i loop –

+1

tra l'altro, StringBuilder sono mutabili, non c'è bisogno di fare questo 'wholeDocument = wholeDocument.append (" \ n "+ line);', usa semplicemente 'wholeDocument.append (" \ n "+ line);' –

+0

Cosa fa 'commit()'? –

risposta

1

Io uso la risposta di @RookieGuy. stackoverflow.com/questions/14581865/hibernate-commit-and-flush

Io uso

session.flush(); 
session.clear(); 

e finalmente dopo leggere tutti i documenti e memorizzarli nel database

tx.commit(); 
session.close(); 

e cambiare

wholeDocument = wholeDocument.append("\n" + line); 

a

wholeDocument.append("\n" + line); 
+0

Penso che tu abbia risposto alla tua domanda. E per me la risposta dovrebbe risolvere il tuo problema. Se risolvesse il tuo problema, potresti accettare la tua risposta. – Atul

0

Io non sono molto sicuro circa la struttura della vostra file.It dati sarà facile da capire, se si potesse fornire un campione del file.

La causa principale dell'utilizzo della memoria è il modo di leggere/iterare il file. Una volta che qualcosa viene letto, resta in memoria. Si consiglia di utilizzare o java.io.FileInputStream o org.apache.commons.io.FileUtils.

Ecco un codice di esempio per iterare con java.io.FileInputStream

try (
     FileInputStream inputStream = new FileInputStream("/tmp/sample.txt"); 
     Scanner sc = new Scanner(inputStream, "UTF-8") 
) { 
    while (sc.hasNextLine()) { 
     String line = sc.nextLine(); 
     addBodyToDatabase(line); 
    } 
} catch (FileNotFoundException e) { 
    e.printStackTrace(); 
} catch (IOException e) { 
    e.printStackTrace(); 
} 

Ecco un codice di esempio per iterare con org.apache.commons.io.FileUtils

File file = new File("/tmp/sample.txt"); 
LineIterator it = FileUtils.lineIterator(file, "UTF-8"); 
try { 
    while (it.hasNext()) { 
     String line = it.nextLine(); 
     addBodyToDatabase(line); 
    } 
} finally { 
    LineIterator.closeQuietly(it); 
} 
0

Si dovrebbe iniziare una transazione, fare l'operazione di salvataggio e commit di una transazione . (Non iniziare una transazione dopo il salvataggio!). Puoi provare a usare StatelessSession per escludere il consumo di memoria da parte di una cache.

e un uso più meno valore, per esempio 20, in questo codice

if (counter % 20 == 0) 

Si può provare a passare StringBuilder come argomento di un metodo per quanto possibile.

8

Prima di tutto è necessario applicare un approccio fork-join qui.

L'attività principale analizza il file e invia batch di un massimo di 100 articoli a uno ExecutorService. Lo ExecutorService dovrebbe avere un numero di thread di lavoro uguale al numero di connessioni di database disponibili. Se si dispone di 4 core CPU, supponiamo che il database possa prendere 8 connessioni simultanee senza fare molto per il cambio di contesto.

È quindi necessario configurare un connection poolingDataSource e avere un valore minSize uguale a maxSize e uguale a 8. Provare HikariCP o ViburDBCP per il pool di connessioni.

Quindi è necessario configurare JDBC batching. Se stai usando MySQL, il generatore IDENTITY disabiliterà il bagno. Se si utilizza un database che supporta le sequenze, assicurarsi di utilizzare anche i generatori di identificatori avanzati (sono l'opzione predefinita in Hibernate 5.x).

In questo modo il processo di inserimento entità viene parallelizzato e disaccoppiato del thread di analisi principale. Il thread principale dovrebbe attendere che ExecutorService termini l'elaborazione di tutte le attività prima di arrestarsi.

2

In realtà è difficile consigliarti senza fare profilazione reale e scoprire cosa rende il tuo codice lento o inefficiente.

Tuttavia ci sono molte cose che possiamo vedere dal codice

  1. Stai utilizzando StringBuilder inefficiente

    wholeDocument.append("\n" + line); dovrebbe essere scritto come wholeDocument.append("\n").append(line); invece

    Perché quello che originale ha scritto verrà tradotto dal compilatore allo whileDocument.append(new StringBuilder("\n").append(line).toString()). Si può vedere quanto StringBuilder s inutile aver creato :)

  2. Corrispettivo in usando Hibernate

    io non sono sicuro di come gestire il vostro session o come è stato implementato il vostro commit(), suppongo che voi avete fatto bene, ci sono ancora più cose da considerare:

    • Avete impostato correttamente la dimensione del batch in Ibernazione? (hibernate.jdbc.batch_size) Per impostazione predefinita, la dimensione del batch JDBC è di circa 5. Si consiglia di assicurarsi di impostarla in dimensioni maggiori (in modo che Hibernate internamente invierà inserti in un batch più grande).

    • Dato che non è necessario le entità in cache di 1 ° livello per un uso successivo, si consiglia di fare la sessione intermittente flush() + clear() a

      1. inserti trigger lotti di cui al punto precedente
      2. chiaro fuori primo livello di cache
  3. Passa da Hibernate per questa funzione.

    Hibernate è bello ma non è la panacea per tutto. Dato che in questa funzione stai solo salvando i record nel DB in base al contenuto del file di testo. Né è necessario alcun comportamento di entità, né è necessario utilizzare la cache di primo livello per l'elaborazione successiva, non vi sono molti motivi per utilizzare Hibernate in questo caso, a causa dell'ulteriore sovraccarico di elaborazione e spazio. Fare semplicemente JDBC con la gestione manuale dei lotti ti farà risparmiare un sacco di problemi.

Problemi correlati