# Wie geschickt größere Datenmengen unterbringen?



## Jens81 (25. Aug 2009)

Hallo zusammen,

ich habe folgendes (Performance) Problem:

Ich lese aus einer "Datenbank" (SAS Datei) via JDBC einige Datensätze aus (ca. 1,5 mio). Die Verbindung zur Datenbank sowie die SQL Abfrage laufen zügig durch. Nur wie speichere ich die Daten geschickt in meiner Java Anwendung. Bisher habe ich die Datensätze über eine while Schleife in einen Vector geschrieben. Das scheint allerdings nicht gerade ideal zu sein, da das Füllen ca. 30-40 Sekunden dauert.

Gibt es schnellere Datenstrukturen (LinkedList oder ArrayList haben keine entscheidende Verbesserung gebracht)? Oder bessere Methoden zum füllen? Oder irgendwelche anderen guten Ideen? Bin für alle Tipps und Vorschläge offen 

Danke und Gruß,
Jens


----------



## SlaterB (25. Aug 2009)

teste mal, ob die Hälfte/  ein Zehntel der Datensätze proportional gleich viel Zeit benötigen 
oder bedeutend schneller gehen,

ein bisschen kannst du dir sparen, indem du dem Vector/ der ArrayList am Anfang gleich mitteilst, wieviele Datensätze es sein werden (Konstruktor)


----------



## Jens81 (25. Aug 2009)

Ich weiß leider vorher nicht, wieviele Ergebnisse zurückkommen. Von daher ist das mit der festen Größe leider nicht wirklich möglich.
Weniger Datensätze brauchen entsprechend weniger Zeit, grob gesagt pro 42.000 Datensätzen 1 Sekunde. Das schwankt allerdings, je nach Rechnerauslastung.


----------



## SlaterB (25. Aug 2009)

tja, so schlecht klingt das gar nicht, anderseits habe ich mal eben in einen Test-Programm 400.000 Dummy-Objekte in 0.2 Sek erzeugt,
was kann man da noch raten, hmm,

erzählt doch einfach möglichst genau mit Code, was alles bei einer Objekt-Speicherung passiert, muss nur ein Container-Objekt erstellt und dann Parameter übertragen werden
oder geschehen komplizierte Berechnungen, werden Strings erzeugt ("nr "+i), LogMeldungen ausgegeben, ..?

lasse JDBC und die restliche Anwendung weg, überlege dir einen Satz Testdaten und erzeuge daraus x00.000 identische Objekte und sammle diese in einer Liste, schneller?

wenn nicht, dann könntest du dieses Testprogramm zumindest posten 

wenn doch, durchlaufe das JDBC-ResultSet ohne die Daten umzuwandeln/ in der Liste zu speichern,
immer noch > 30 Sek? dann ist deine Liste wohl weniger das Problem


----------



## sparrow (25. Aug 2009)

Mach den Versuch mal ohne die Tupel in eine Collection zu speichern.
Quasi nur Datensätze des ResultSet durchlaufen ohne etwas damit zu tun, einfach um diese Stelle als Nadelöhr auszuschließen.


----------



## ARadauer (25. Aug 2009)

du ließt während du füllst, oder? also für 1.5 mio Datensätze... 40 Sekunden.. finde ich jetzt echt human. ich kenn systeme die brauchen dafür 3 tage ;-)

zeig mal ein bisschen code...


----------



## robertpic71 (25. Aug 2009)

Jens81 hat gesagt.:


> Ich weiß leider vorher nicht, wieviele Ergebnisse zurückkommen. Von daher ist das mit der festen Größe leider nicht wirklich möglich.



Mir sagt jetzt SAS nichts, aber normalerweise kann man bei jeder Datenbank vorher den gleichen SELECT mit COUNT(*) machen und hat dann die Größe für die z.B. ArrayList.

/Robert


----------



## SlaterB (25. Aug 2009)

die genaue Anzahl ist auch nicht so wichtig,
wenn man auf paar MB Speicher verzichten kann, dann könnte man die ArrayList mit 3 Mio. inialisieren,
anderenfalls würden auch kleine Werte wie 100.000 bisschen helfen, selbst wenn es später 1.x Millionen werden

Hintergrund ist, dass die Liste sonst anfangs nur 16 Plätze hat, bei mehr Daten auf 32, 64, 128, .. vergrößert wird, vorhandene Daten müssen umkopiert werden,
so viel Aufwand ist das nicht, bei gerade mal 15 Verdopplungen ist man schon in den Millionen,
aber mit wenigen Zeichen Quellcode kann man da paar ms Zeit sparen

edit: statt verdoppelt, wird der Platz nur um 50% erhöht, zumindest bei ArrayList


----------



## Jens81 (25. Aug 2009)

Hab mal ein kleines Testprog erstellt


```
public class Analyse {
	
	public void performTest() {
		Zeitmesser z0 = new Zeitmesser("Verbindung zu SAS aufbauen");
		Zeitmesser z1 = new Zeitmesser("Daten aus Tabelle holen");
		Zeitmesser z2 = new Zeitmesser("Daten in Vector schreiben");
		
		String tabelle = "test";
		String abfrage = "SELECT spalte FROM kundw." + tabelle + " WHERE flg = 0";
		Vector erg = new Vector();
		Connection verbindung;
		Statement statement;
		String pfad = "librefs=datModel 'pfad'";
		SASConnection sc = new SASConnection();
		
		z0.starteMessung();
		verbindung = sc.openDataConnection(pfad, "name", "pass");
		z0.beendeMessung();
		try {
			z1.starteMessung();
			statement = verbindung.createStatement();
			ResultSet result = statement.executeQuery(abfrage);
			z1.beendeMessung();
			
			z2.starteMessung();
			while (result.next()) 
			        erg.addElement(result.getString(1));
			
			
			z2.beendeMessung();
			

		}
		catch (Exception sql) {
			
		}
		
		System.out.println(erg.size());
	}
	
	public static void main(String[] args) {
		Analyse t = new Analyse();
		t.performTest();
	}
}
```



robertpic71 hat gesagt.:


> Mir sagt jetzt SAS nichts, aber normalerweise kann man bei jeder Datenbank vorher den gleichen SELECT mit COUNT(*) machen und hat dann die Größe für die z.B. ArrayList.
> 
> /Robert



Die Frage ist nur, ob der zusätzliche Aufwand eine Verbesserung bringt. Aber ich kann's ja mal testen.

EDIT: Das try catch in der Schleife war nur der Rest von nem Test, hab's entfernt..


----------



## SlaterB (25. Aug 2009)

das try/ catch in der Schleife kann weg,
ansonsten niemand das wirklich testen, lasse es selber ohne Vector durchlaufen


----------



## Jens81 (26. Aug 2009)

Morgen zusammen,

hab das ResultSet durchlaufen lassen, ohne die Daten in den Vector zu schreiben. Siehe da, es läuft nicht schneller (oder zumindest nicht entscheidend). Von daher war euer Ansatz schonmal sehr gut 
Stellt sich natürlich die Frage, wie das ResultSet schneller durchlaufen werden kann ???:L

EDIT: Selbst wenn ich in der while Schleife gar keine Anweisung mehr stehen habe, läuft diese > 30 Sekunden.

EDIT2: Der Falschenhals scheint result.next() zu sein. Nur was dagegen tun?


----------



## ARadauer (26. Aug 2009)

ich denke du hast gar kein Problem... du ließt 1.5 Mio Datensätze aus einer Datenbank, wie schnell soll das gehen? 

Liegt die Datenbank auf deinem Rechner? SAS? Was ist das, ich kenn das nicht... vielleicht geht das nicht schneller?


----------



## Jens81 (26. Aug 2009)

Bis eben dachte ich, dass die Daten bereits komplett im ResultSet liegen. Es hat mich daher gewundert, warum das Durchlaufen so lange dauert und das es schneller gehen müsste.

Aber anscheinend ist es so, dass die Daten erst durch next() geladen werden... 

Es hätte ja sein können, dass man an der Performance noch was machen kann  1x 40 Sekunden warten ist nicht so das Problem, aber wenn ich Zugriffe auf mehrere Tabellen ähnlicher Größe durchführen muss, wäre es inakzeptabel 15mins warten zu müssen :-/


PS: SAS steht für Statistical Analytic System (SAS | Business Analytics and Business Intelligence Software)


----------



## sparrow (26. Aug 2009)

Jens81 hat gesagt.:


> Morgen zusammen,
> 
> hab das ResultSet durchlaufen lassen, ohne die Daten in den Vector zu schreiben. Siehe da, es läuft nicht schneller (oder zumindest nicht entscheidend). Von daher war euer Ansatz schonmal sehr gut
> Stellt sich natürlich die Frage, wie das ResultSet schneller durchlaufen werden kann ???:L



Das ResultSet wird genau so schnell durchlaufen wie die Datenbank die Tupel selektiert. Da ist also der Flaschenhals.
Ich weiß leider nicht was SAS ist... aber anscheinend nicht schnell.
Normalerweise funktioniert es so, dass du eine Anfrage an einen Server sendest und sich dann die Datenbank darum kümmert die möglichst schnell die Daten zur Verfügung zu stellen. Dafür ist die Datenbank ja da.
Wenn du unbedingt in eine Datei speichern willst empfehle ich dir Derby oder HSQLDB, mit denen habe ich sehr gute Erfahrung gemacht.

Gruß
sparrow


----------



## Jens81 (26. Aug 2009)

Speichern will ich nichts, es geht nur um's auslesen.


----------



## sparrow (26. Aug 2009)

Jo, dafür sind die Daten, laut deiner Aussage, aber in einer Datei gespeichert und nicht etwa auf einem Datenbankserver der einen Datenbankdienst anbietet.


----------



## Jens81 (26. Aug 2009)

Ja, die SAS Datenbank nutzt Dateien. Diese liegen auf dem Server.

Ich werde mal testen, ob mehrere Abfragen, die jeweils nur einen Teil der Daten selektieren und "parallel" laufen, eine bessere Performance zeigen.


----------



## ARadauer (26. Aug 2009)

> wäre es inakzeptabel 15mins warten zu müssen


also wie viele solche Tabellen hast du 20? dann reden wir von 30 Mio Datensätze.

30 Millionen Datensätze in 15 Minuten verarbeiten ist meiner Meinung nach schon akzeptabel ;-)

Was machst du eigentlich genau mit den Daten?


----------



## velaluka (26. Aug 2009)

Hallo,
wenn es doch anscheinend an dem JDBC Treiber liegt, würde ich mal nachsehen was dein SAS Treiber für Performance Tuning Möglichkeiten bietet. Eventuell kannst du so noch etwas rausholen.
Für Oracle würden mir dazu dazu die Stichworte
Batch, Prefetching und Transaktion Mode einfallen. Allerdings müsste diese Dinge schon implementiert sein.(Keine Ahnung ob der SAS Treiber sowas kann).
Oder eventuell einen Cache(z.B. EH-Cache) falls du wenigstens bei längeren Laufen der Appikation Performance Verbesserungen erreichen willst.

Ciao velaluka


----------



## sparrow (26. Aug 2009)

Denke nicht, dass es dadurch schneller wird.
Ob nun sequentiell oder parallel, die Datensätze müssen trotzdem gefunden werden.


----------



## Jens81 (26. Aug 2009)

Wenn ich die Daten über 5 Threads einsammle, kann ich die Zeit immerhin von zuvor zwischen 31 und 54 Sekunden (im Schnitt 36 Sek) auf 21-22 Sekunden drücken. Dabei verteile ich die Daten (noch) nicht gleichmäßig, dass werde ich mal im nächsten Schritt testen.

Der SAS JDBC Treiber unterstützt leider nichts in dieser Richtung, das habe ich schon versucht 

edit: die Daten brauche ich für eine Data Mining Anwendung; diese speziell für ein Häufigkeitsdiagramm und die Verteilungsfunktion.


----------



## velaluka (26. Aug 2009)

Hallo Jens,

auch der hier https://support.sas.com/rnd/web/intrnet/java/javadoc/com/sas/rio/MVAStatement.html#setFetchSize(int) hilft dir nicht weiter..... 
Immer diese halben Treiber Implementierungen :-(
Ciao velaluka


----------



## Jens81 (26. Aug 2009)

Ein gleichmäßiges Verteilen der Datenmengen bringt einen geringeren Performanceschub. Die Thread-Lösung werde ich dann erstmal so in meine Anwendung integrieren.

Danke an alle für eure Ideen und Hilfestellungen!

Gruß, Jens


----------

