# Arbeiten mit sehr großen CSV Dateien



## nrg (15. Feb 2011)

Hallo Zusammen,

ich weiß, ähnliche Themen gab es hier in letzter Zeit öfters aber ich mache ein neues auf, weil ich mein Problem in den anderen nicht direkt wiederfinden konnte .

Ich lese große CSV-Dateien ein (500.000 Datensätze). Jeder Datensatz ist ein Element einer 
	
	
	
	





```
List<String>
```
. Diese splitte ich dann in eine 
	
	
	
	





```
List<String[]>
```
, d.h. ich hätte bereits rund 1Mio Objekte. Warum ich das so machen muss, hat den Hintergrund, weil die komplette Datei zu jedem Datensatz, der verarbeitet wird, zugänglich sein muss. Es kann sein, dass bei Datensatz 400000 auf den ersten zugegriffen wird. Ausserdem erfolgt bei jedem Datensatz eine Prüfung. Liefert diese 
	
	
	
	





```
false
```
, wird der aktuelle Datensatz einer Errorliste hinzugefügt. D.h. ich brauch im Grunde beide Listen zu jeder Zeit.
Dazu kommt am Ende natürlich noch der Output.

Einzige Option, die ich hier sehe ist, die 
	
	
	
	





```
List<String>
```
 mit einem Iterator zu durchlaufen und wenn ein Datensatz erfolgreich bearbeitet wurde, diesen zu entfernen. Dadruch würde die HeapSize schonmal im Laufe des Prozess wenigstens stagnieren (1 Datensatz Input = 1 Datensatz Output bzw. 1 Errordatensatz) - in manchen Fällen würde die HeapSize auch abnehmen.

Nun zur Frage: Was bleibt mir anderes übrig, als die HeapSize massiv hochzusetzen?
Ändere ich den Ablauf (direkter Split beim Lesen) verzichte ich auf einige Funktionen des Programms, auf die leider kein Verzicht ist. Die Möglichkeit beim Lesen direkt zu Splitten und die Bedingung zu prüfen (habe mir das schon in Form eines Interfaces 
	
	
	
	





```
EntityFilter
```
 mit 
	
	
	
	





```
accept(String[] entity)
```
 überlegt) funktioniert leider auch nicht ganz, weil zum Zeitpunkt der Prüfung die komplette 
	
	
	
	





```
List<String[]>
```
 zur Verfügung stehen muss.

Danke schonmal
Grüße
Andi


----------



## XHelp (15. Feb 2011)

Was soll denn anschließend passieren? Du kannst z.B. eine Datenbank einsetzen um nicht alle Einträge im Speicher zu halten, sondern nur die, die du im Moment benötigst.


----------



## nrg (15. Feb 2011)

XHelp hat gesagt.:


> Was soll denn anschließend passieren?



nach was? 



XHelp hat gesagt.:


> Du kannst z.B. eine Datenbank einsetzen um nicht alle Einträge im Speicher zu halten, sondern nur die, die du im Moment benötigst.



Hatte ich mir auch schon überlegt aber soll eigentlich komplett Standalone laufen.


----------



## HoaX (15. Feb 2011)

h2, hsqldb, ... kannst du direkt einbetten, oder was meinst du mit stand alone?


----------



## XHelp (15. Feb 2011)

nrg hat gesagt.:


> nach was?



Naja, du wirst das ganze ja nicht machen, weil es einfach nur Spass macht CVS Dateien einzulesen. Die Verarbeitung passiert doch bestimmt aus einem Grund. Wenn du z.B. mit den Daten im Programm arbeiten muss und damit du die Daten da hast, musst du die jedes mal beim Programmstart einlesen, dann wäre es wirklich sinnvoll eine eingebettete Datenbank zu nutzen.


----------



## nrg (15. Feb 2011)

Naja da handelt es sich im Grunde um einen Konverter. Auf der einen Seite kommt eine CSV rein, wird verarbeitet und ein Output erzeugt (ggf. + Erroroutput). Der Input variiert immer und somit komme ich auch nicht daran vorbei das auch immer wieder neu einzulesen. Wenn ich die Möglichkeit hätte, direkt von Datenbanken zu lesen, würde ich das tun (ist im Converter implementiert nur leider lässt das nicht immer das Kundensystem zu). 

Mit Standalone meine ich, dass das Programm ganz ohne jegliche Datenanbindung o.ä. funktioniert. Es soll eben möglichst einfach gestrickt sein. Wenn ich die Möglichkeit hätte performant die Daten auf der Platte zwischenzuspeichern und darauf zuzugreifen (sei es über SQL oder Hash), wäre das vielleicht noch eine Option. Jetzt kommt sogar noch das Reverenzlesen aus anderen Dateien dazu. Die Funktionalität ist geschaffen nur wird der Speicher ja jetzt schon knapp. Dafür darf ich dann nochmal eine Datei in eine HashMap laden, um performant darauf zugreifen zu können (auch hier wär mir eine DB lieber - nur leider lässt das der Kunde nicht zu)

Ich habe nur oft gelesen, dass ein heraufsetzen der HeapSize meinst konzeptionelle Probleme voraussetzt und man das i.d.R. nicht braucht. Für meine Einschätzung gibt es aber keine Ressourcen im Prozess auf die ich Verzichten könnte.


----------



## XHelp (15. Feb 2011)

Ja, aber du musst ja jetzt nicht jedes mal einen MySQL Server einrichtigen... kannst doch eine eingebettete DB nehmen, der Kunde sieht dann nix von.


----------



## nrg (15. Feb 2011)

Genau sowas könnte ich mir vorstellen.

Also ich les die Datei ein und speicher die einfach in einer Datenbank zwischen - die liegt dann im classpath einfach in Form einer Kontainerdatei. Nun kann ich auf diesen Kontainer Querys ausführen. Das wäre eine denkbare Alternative für mich .

Kann HyperSQL sowas? Ein Kollege hat mir grad Perst vorgeschlagen (edit: empfohlen nicht wirklich, weil er selbst damit noch nicht viele Erfahrungen gesammelt hat). Hat jemand Erfahrungen damit?


----------



## maki (15. Feb 2011)

HyperSQl == HSQLDB == HSQL

Wurde auch schon vorgeschlagen... H2 und Derby/JavaDB können das auch


----------



## nrg (15. Feb 2011)

maki hat gesagt.:


> HyperSQl == HSQLDB == HSQL


 
ja. war eben auf HoaX Post bezogen 




maki hat gesagt.:


> H2 und Derby/JavaDB können das auch



werd ich mir mal anschauen *edit*: auch persönliche Vorlieben oder Empfehlungen nehme ich gerne mit. Also wenn jemand vielleicht bereits aus Erfahrung sagen kann, dass sich das eine mehr das andere weniger dafür eignet. Soll wirklich lightweight sein. Also am liebsten eine .jar in ClassPath und fertig 


Jetzt noch eine Frage: wäre Serialisierung vielleicht auch eine Alternative oder muss ich da vielleicht schon wieder Performance einbüßen?

Ansonsten Danke allen für die Hilfe


----------



## XHelp (15. Feb 2011)

Serialisierung ist eine andere Sache.


----------



## nrg (15. Feb 2011)

Naja dadurch könnte ich ja den Heap zur Laufzeit entlasten und ggf. Objekte holen, wenn ich es benötige. Aber glaube ehrlich gesagt nicht, dass der Lese- und Schreibaufwand mit der Performance einer embedded db mithalten kann. war nur eine Überlegung


----------



## LoR (15. Feb 2011)

Wenn es möglich ist, dann stell die Datei mal zum Download bereit. Eventuell findet es sich dann eine bessere Alternative als eine DB. Datenbanken sind meist zuviel Overhead und nur bei wirklich sehr großen Datenmengen erste Wahl.


----------



## nrg (15. Feb 2011)

nachdem das kundendaten sind, wird das leider nicht gehen. Aber im Grunde eine ganz normale CSV-Datei


----------



## bone2 (15. Feb 2011)

kannst du nicht direkt 1 zeile lesen, bearbeiten/infos rausziehen , schreiben usw?
wenn du so auf element 400k zugreifen musst, les doch in dem moment einfach zeile 400k ein. gibt es einen grund warum du alle 500k elemente im speicher haben musst?


----------



## nrg (15. Feb 2011)

der Converter ist gleichzeitig eine Plattform für mehrere Textschnittstellen. Um alle abzudenken, habe ich mir überlegt, die Dateien einfach aufzubereiten und die Daten einer ScriptEngine zu übergeben. Jetzt kann man zum einem auf den aktuellen Datensatz in Form eines Arrays zugreifen und zum anderen auf beliebige Felder der kompletten Datei. Zusätzlich kann ich natürlich ganz einfach Utility-Funktionen hinzufügen oder Datenbankzugriffe ermöglichen)


```
feld1 = field[10] #das erste feld der ausgabezeile = das 11 feld der aktuellen eingabezeile
feld2 = index.get(row+2, 10) #das zweite feld der ausgabezeile = das 11 feld der aktuellen eingabezeile+2
feld3 = util.getTimeStamp("yyyy") #aktuelles Jahr
.....
```

Es werden auch nicht alle Zeilen verarbeitet. eine Satzbeschreibung sieht z.B. so aus


```
s;002;bla;gesamtbetrag;kst
g;betrag1;ktr
g;betrag2;ktr
g;betrag3;ktr
g;betrag4;ktr
```

hier interessieren mich z.b. nur alle Sätze, die mit S anfangen. d.h. ich habe in meinen Properties z.b. eine accept_condition definiert. 
	
	
	
	





```
field[0] == "s"
```
; wenn diese 
	
	
	
	





```
true
```
 liefert, wird der Datensatz überhaupt bearbeitet. Dann wird die parse_condition abgeprüft (z.b: 
	
	
	
	





```
field.length == 5
```
, weil die Qualifier auch gern mal wie Kraut und Rüben sind und somit manchmal quatsch raus kommt). Wenn diese nicht erfüllt ist, wird der aktuelle Datensatz bis zum nächsten Datensatz, der die accept_condition erfüllt in eine Errorliste aufgenommen (also hier Datensatz 1-5, 6 wäre ja dann wieder ein s-satz).
Die g-Sätze kann ich natürlich nicht ignorieren, weil es gut sein kann, dass man mit 
	
	
	
	





```
index.get(row+2, 10)
```
 darauf zugreift.

So ist z.b. die Logik. Sind wirklich teilweiße sehr exotische und ständig wechselnde Wünsche der Kunden, wodurch ich mit der Dynamik einer propertiesgesteuerten ScriptEngine am besten dran bin.

Das funktioniert alles ja auch wunderbar nur jetzt kommt der nächste mit 500.000 Datensätze.
Option a: HeapSize massiv erhöhen
Option b: Suche ich grad 

ich schau mir grad HyperSQL und H2 an. Mal sehen ob ich das auf den Datensatzprozess der CSV-Dateien übertragen kann. Gerade bei H2 auch gesehn, dass es dort CSVREAD statements gibt. Somit könnte ich vielleicht das Referenzlesen aus anderen Dateien outsourcen (was derzeit in einer HashMap für einen schnellen Zugriff gespeichert wird - was natürlich auch wieder massig HeapSize schluckt).

Ist etwas viel aus einmal aber vielleicht hat ja jemand noch einen Tipp. Ich werde es auf jedenfall mal mit einer embedded DB probieren. Im Zweifelsfall muss halt die HeapSize hoch


----------



## Empire Phoenix (15. Feb 2011)

Würde embedded db amchen oder das ganze als array/Hashmap mit SoftReferences umsetzen, die bei bedarf aus der Datei regeneriert werden können.


----------



## nrg (15. Feb 2011)

Empire Phoenix hat gesagt.:


> das ganze als array/Hashmap mit SoftReferences umsetzen, die bei bedarf aus der Datei regeneriert werden können.



bei dem Speicherbedarf würde der GC mir alle SoftReferenzen nach 30% Process kicken. Da kann ich dann soviel aus der Datei regenerien, dass das imho in keinem Verhältnis mehr steht.

werd mich wohl mal an embedded db rantasten. am liebsten wäre mir eine möglichkeit die CSV einfach schnell in Tabellenform abzulegen und dann mit get(row, column) darauf zugreifen zu können (*edit*: wobei das mit normalen selects und einer id spalte ja ohne probleme projizierbar ist). eignet sich da vielleicht eine library besonders? ansonsten werd ich wohl mal mit h2 oder hsql anfangen.


----------



## nrg (15. Feb 2011)

hab mich jetzt etwas in h2 reingelesen. hört sich genau nach dem an, was ich brauche. eventuell auch in verbindung mit hibernate. vielen dank für die hilfe


----------



## HoaX (15. Feb 2011)

Solange es nur um solch triviales Datengeschubse geht würde ich Hibernate da raus lassen. Wenn man keine Übung damit hat, kann das schnell zur Performacebremse werden.


----------



## nrg (16. Feb 2011)

eine Frage hätte ich noch. Wie bekomme ich ein java.sql.Array Array (Java Platform SE 6) in ein Object[] ? Ich hab dort nur eine Methode Object getArray(), hätte aber eher eine Object[] getArray() erwartet . Geschrieben wurd das Array mit einem PreparedStatement. Wenn ich mir das Object, das das ResultSet liefert im Debugger ankucke, sieht das schon sehr nach einen Array aus. Muss ich jetzt ersthaft davon wieder ein ResultSet holen und da drüberiterieren?


----------

