2016-01-12 12 views
6

Ich lese Datei und erstellen Sie ein Objekt von ihm und speichern in Postgresql-Datenbank. Meine Datei hat 100.000 Dokumente, die ich aus einer Datei gelesen und geteilt habe und schließlich in der Datenbank gespeichert habe. Ich kann List<> nicht erstellen und alle Dokumente in List<> speichern, weil mein RAM wenig ist. Mein Code zum Lesen und Schreiben in die Datenbank lautet wie folgt. Aber My JVM Heap füllt und kann nicht mehr Dokumente speichern. Wie man Datei liest und effektiv in der Datenbank speichert.Wie Sie Daten mit Hibernate so schnell wie möglich einfügen

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

Vielleicht brauchen Sie 'String wholeDocument = new Stringbuilder() hinzuzufügen;' irgendwo in Ihrem Schleifen –

+1

durch die Art und Weise, String wandelbar sind, brauchen Sie nicht diese 'wholeDocument = wholeDocument.append zu tun (“ \ n "+ line);', verwende einfach 'wholeDocument.append (" \ n "+ Zeile);' –

+0

Was macht 'commit()'? –

Antwort

1

Ich benutze @RookieGuy Antwort. stackoverflow.com/questions/14581865/hibernate-commit-and-flush

Ich benutze

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

und schließlich, nachdem alle Dokumente lesen und speichern sie in die Datenbank

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

und

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

zu

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

Ich denke, Sie haben Ihre Frage beantwortet. Und für mich sollte die Antwort Ihr Problem lösen. Wenn es Ihr Problem gelöst hat, möchten Sie vielleicht Ihre eigene Antwort akzeptieren. – Atul

0

Ich bin nicht sehr sicher über die Struktur Ihrer Daten file.It wird leicht zu verstehen, wenn Sie eine Probe Ihrer Datei zur Verfügung stellen könnten.

Die Hauptursache für den Speicherverbrauch ist die Art des Lesens/Iterierens der Datei. Sobald etwas gelesen wird, bleibt es in Erinnerung. Sie sollten lieber java.io.FileInputStream oder org.apache.commons.io.FileUtils verwenden.

Hier ist ein Beispielcode mit 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(); 
} 

Hier iterieren ist ein Beispielcode mit 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

Sie eine Transaktion beginnen sollte laufen, tun die Operation speichern und eine Transaktion verpflichten . (Starten Sie nach dem Speichern keine Transaktion!). Sie können versuchen, StatelessSession zu verwenden, um Speicherverbrauch durch einen Cache auszuschließen.

und verwendet mehr weniger Wert, als Beispiel 20, in diesem Code

if (counter % 20 == 0) 

Sie können versuchen, StringBuilder als Methode Argument so weit wie möglich zu passieren.

8

Zunächst sollten Sie einen Ansatz fork-join hier anwenden.

Die Hauptaufgabe analysiert die Datei und sendet Stapel von höchstens 100 Elementen an eine ExecutorService. Die ExecutorService sollte eine Anzahl von Worker-Threads haben, die der Anzahl der verfügbaren Datenbankverbindungen entspricht. Wenn Sie 4 CPU-Kerne haben, nehmen wir an, dass die Datenbank 8 gleichzeitige Verbindungen nehmen kann, ohne zu viele Kontextwechsel durchzuführen.

Sie sollten dann eine connection pooling konfigurieren und haben eine MinSize gleich MaxSize und gleich 8. Versuchen Sie HikariCP oder ViburDBCP für Verbindungspooling.

Dann müssen Sie JDBC batching konfigurieren. Wenn Sie MySQL verwenden, wird der IDENTITY-Generator das Baden deaktivieren. Wenn Sie eine Datenbank verwenden, die Sequenzen unterstützt, stellen Sie sicher, dass Sie auch die Generatoren für erweiterte Bezeichner verwenden (dies sind die Standardoptionen in Hibernate 5.x).

Auf diese Weise wird der Entity-Insert-Prozess vom Hauptparsing-Thread parallelisiert und entkoppelt. Der Hauptthread sollte warten, bis die ExecutorService die Verarbeitung aller Aufgaben abgeschlossen hat, bevor sie heruntergefahren wird.

2

Eigentlich ist es schwierig, Ihnen Vorschläge zu machen, ohne ein echtes Profiling durchzuführen und herauszufinden, was Ihren Code langsam oder ineffizient macht.

Allerdings gibt es einige Dinge, die wir aus dem Code

  1. Sie Stringbuilder verwenden, sehen uneffizient

    wholeDocument.append("\n" + line); sollte als wholeDocument.append("\n").append(line); statt

    geschrieben werden, weil, was Sie ursprünglich übersetzt wird wrote durch Compiler zu whileDocument.append(new StringBuilder("\n").append(line).toString()). Sie können sehen, wie viel unnötige StringBuilder s Sie erstellt haben :)

  2. Berücksichtigung in Hibernate

    Ich bin nicht sicher, wie Sie bei der Verwaltung Ihrer session oder wie Sie Ihre commit() implementiert, ich nehme an, Sie haben es richtig gemacht, Es gibt noch mehr Dinge zu beachten:

    • Haben Sie die Batchgröße in Hibernate ordnungsgemäß eingerichtet? (hibernate.jdbc.batch_size) Standardmäßig ist die JDBC-Batch-Größe etwas um 5. Sie können sicherstellen, dass Sie eine größere Größe festlegen (so dass intern Hibernate Einsätze in einem größeren Batch sendet).

    • Vorausgesetzt, dass Sie für die spätere Verwendung der Entitäten in der 1.-Level-Cache nicht benötigen, können Sie in den vorherigen Punkt

    • out erwähnten intermittierenden Sitzung flush() + clear() zu

      1. Trigger-Batch-Einsätze tun
      2. klar Cache erste Ebene
  3. Wechsel von Hibernate weg für diese Funktion.

    Hibernate ist cool, aber es ist kein Allheilmittel für alles. In diesem Feature speichern Sie nur Datensätze in der DB basierend auf dem Inhalt der Textdatei. Sie benötigen weder Entitätsverhalten noch müssen Sie den Cache der ersten Ebene für die spätere Verarbeitung verwenden. Es gibt nicht viel Grund, hier Hibernate zu verwenden, da zusätzlicher Verarbeitungsaufwand und Platzbedarf anfallen. Einfaches JDBC mit manueller Stapelverarbeitung spart Ihnen eine Menge Ärger.

Verwandte Themen