# ResultSet, Performance



## Schreihalz (11. Apr 2005)

Hallöchen allerseits...

Ich habe folgendes Problem:
3 Tabellen werden in der Datenbak ausgelesen (Oracle). Alle 3 Selects sind PreparedStatements. Das Auslesen geschieht  parallel in 3 Threads. Jeder Thread hat jeweils eine Connection und 1 PrepareStatement. Mein Ziel ist es: das Auslesen so schnell wie möglich zu machen, nur irgendwie klappt das nicht ganz. Die selects haben jeweils so um die 20 Spalten und zur zeit werden so um die 500 Zeilen Selected. Die Zeiten die ich erziele schwanken sehr stark von
* 100 ms bis 2 sek*.
die 3 Threads stocken manchmal richtig an der Stelle, wo ich die ResultSets in die Container-Objekte überführe.

als Beispiel für eine Tabelle einwenig Quellcode:

im Thread:


```
private void fillAccounts() {
        try {
            ResultSet rs_accounts = prep_selectAccounts.executeQuery();
            while (rs_accounts.next()) {
                [b]AccountContainer accCont = new AccountContainer(rs_accounts);[/b]
                resultList.add(accCont);
            }
            rs_accounts.close();
        } catch (Exception sqle) {
            sqle.printStackTrace();
            //this.log.error(....);
        }
    }
```

Und der Konstruktor, der da aufgerufen wird:


```
public AccountContainer(ResultSet rs_accounts) {
		try{
			this.setBla1(rs_accounts.getDouble("BLA1"));
			this.setBla2(rs_accounts.getString("BLA2"));
			this.setBla3(rs_accounts.getDouble("BLA3"));
                        ..............
			this.setBla21(rs_accounts.getBoolean("BLA21"));
			this.setBla22(rs_accounts.getString("BLA22"));
			this.setBla23(rs_accounts.getDouble("BLA23"));
		}
		catch(SQLException sqle){
			sqle.printStackTrace();
			//this.log.error(sqle);
		}
	}
```

hat denn jemand eine Idee, wie ich es schneller und konstanter bekomme. Ich habe die Vermutung, dass JVM an der Stelle, wo diese Konstruktoren je nach Tabelle in der Schleife bis zu 500 mal (Ist ja eigentlich gar nicht so viel) aufgerufen werden, mit der Verteilung der Ressourcen einfach nicht zurecht kommt oder so... Ich hatte schon versucht, den Threads unterschiedliche Prioritäten zuzuweisen, aber so richtig was hat es nicht gebracht.


----------



## semi (11. Apr 2005)

Sieht eigentlich gut aus. Da gibt es kaum was zu optimieren.
Aber bis zu 2 Sekunden sind schon heftig. Bist Du sicher, dass Du keine 
Table-Locks hast oder dass sich die Threads gegenseitig blockieren?
Wenn die Daten häufig abgefragt werden, wäre ein Cache auch eine Lösung.
Ansonsten versuche vielleicht die Größe der Collection vorher auf einen Erfahrungswert 
zu setzen, damit sie beim Einfügen nicht umkopiert/vergrößert werden muss.
z.B.
	
	
	
	





```
Collection resultList = new ArrayList(500);
```
(Bringt aber nicht besonders viel)

Edit: Noch eine Kleinigkeit. Auf ResultSet über Index (Paar Konstanten dazu schreiben), 
statt Spaltennamen zugreifen. Es erspart ziemlich viele Stringvergleiche in ResultSet.
Wieder paar Millisekunden 
Danach kannst Du vielleicht noch den Aufwand treiben und versuchen die AccountContainer-Objekte
wieder zu verwenden. Stichwort: "Object pool", "Instance pool" etc.


----------



## Schreihalz (11. Apr 2005)

sind doch schon mal paar Ansätze, die man verfolgen kann. Locks können es nicht sein, weil ich noch der einzige bin, der auf die DB zugreift. Ich versuche es mal mit den Indizies, es hört sich vernünftig an, die Stringvergleiche mag ich auch nicht. 

Ein Cache wäre wohl das Sinnvollste. Da fehlt 1. die Erfahrung und das wichtigste: die Zeit  vielleicht findet man sie mal

danke auf jeden Fall


----------



## semi (11. Apr 2005)

Apropos Indizies. Wie performant ist die SQL Abfrage selbst?
Manchmal wirkt ein zusätzlicher Index in der Tabelle oder
eine etwas anders formulierte Abfrage Wunder.


----------



## Schreihalz (11. Apr 2005)

die Abfrage ist sehr schnell.. da besteht kein Grund zur Sorge. Immerhin etwas, worauf man sich ab und zu verlassen kann 
Die Messungen ergeben: die While-schleife verbraucht fast alles, wenns mal langsam wird


----------



## Bleiglanz (11. Apr 2005)

vergiss die threads! "jeder hat eine Connection"???

=> der Verbindungsaufbau selbst ist schon aufwändig, verwende lieber EINE Connection!!

und lies die 3 nacheinander ein, was soll denn die Parallelisierung bringen??

schreib dir eine Stored Proc, die alle drei Ergebnisse auf einmal liefert und verwende dann z.B. statement.getMoreResults (ohne Stored Proc gehts möglicherweise auch mit execute("SELECT ..; SELECT .."))

usw


----------



## Bleiglanz (11. Apr 2005)

Nachtrag: wenn du Java5 hast und dich ein bisschen ins CachedRow Set einarbeiten willst, das ist oft einfacher als die ganzen "Accout-Stellvertreter-Objekte" mit new zu erzeugen

=> schmeiss alles in ein RowSet und mach aus dem AccountContainer einen Proxy (ohne Member!) für die zuständige Zeile...


----------



## semi (11. Apr 2005)

Es gab mal eine Seite www.irongrid.com, wo eine ziemlich gute Cache Lösung
verfügbar war (IronEye Cache). Die Seite ist aber verschwunden. ???:L 
Es wird zwischen den JDBC-Treiber und die Datenbank geschaltet und cacht,
was das Zeug hält. Am Code muss man nix ändern.
Auch das IronTrack SQL war nett, um die Performance der Abfragen zu bestaunen.
Ich habe beides immer noch, falls Du es haben möchtest, aber wenn Du es
in einem komerziellen Projekt verwendest, dann ist auch Kontinuität wichtig
bzw. das die eingesetzten Tools keine Leichen sind, die nicht mehr weiterentwickelt
werden. Hmm... 

Habe gerade noch etwas anderes über Google gefunden http://isocra.com/livestore/,
kann aber nix dazu sagen, kenne es nicht. Ich habe nach "JDBC Cache" gesucht.

Meine Erfahrung ist, dass Strings und Typenkonvertierungen im Allgemeinen die meiste
Zeit schlucken.

Beschränkst Du die Anzahl der Datensätze bei den Abfragen auf 500 Datensätze oder
liest Du was kommt und dann nur die ersten 500? Da wäre nämlich noch was zu optimieren.
ResultSet.setFetchSize(500)


----------



## semi (11. Apr 2005)

Bleiglanz hat gesagt.:
			
		

> Nachtrag: wenn du Java5 hast und dich ein bisschen ins CachedRow Set einarbeiten willst, das ist oft einfacher als die ganzen "Accout-Stellvertreter-Objekte" mit new zu erzeugen
> 
> => schmeiss alles in ein RowSet und mach aus dem AccountContainer einen Proxy (ohne Member!) für die zuständige Zeile...


Gute Lösung. CachedRowSet ist schnell. Auch unter JDK 1.4 verwendbar. Googeln.


----------



## Gast (11. Apr 2005)

1. ich habe kein java5 (1.4.2)
2. Connections werden nur am anfang einmal aufgebaut und nur beim Modulstop geclosed.. (Threads warten nur zwischen den Selects) ich habe mit den 3 Threads auch mal 1 Connection geteilt und die Ergebnisse kann man sich vorstellen (langsamer) deswegen 3 Connections
3. Parallelisierung: Das Dingen muss eben schnell sein, obs man es zum Teil mit Threads erreicht sei mal dahin gestellt, denn ich entscheide nicht alles.

Was mich jetzt aber interessiert:
"statement.getMoreResults ... (ohne Stored Proc gehts möglicherweise auch mit execute("SELECT ..; SELECT ..")) "

kannst du es bitte etwas näher erläutern (also ohne strored proc) ? oder vielleicht nur einpaar stichworte oder links?


----------



## Schreihalz (11. Apr 2005)

gast-posting war von mir.. komischerweise war ich plötzlic hausgeloggt :/
Muss erstma prioritäten setzen wo ich anfange 
Das nächste was ich wohl anstellen werde ist also googeln nach Cache und CachedRowSet


----------



## Bleiglanz (11. Apr 2005)

Threads machen das ganze auf einem 1Prozessor Rechner LANGSAMER, hast du da noch nie etwas davon gehört?

selbst bei einem extrem langsamen netzwerk dürfte durch das switchen zwischen den Threads + IO viel Overhead entstehen...

wenn dreimal parallel rs.next() aufgerufen wird, dann wird ja immer ein anderer Socket bedient, ich kann mir nicht vorstellen, dass 3 threads hier schneller arbeiten als 3 mal hinteireinander alles auf einen Rutsch holen...

zum getMoreResults: oft ist es möglich, dass ein einziger Request an eine DB MEHRERE Resultsets zurückliefert; am einfachsten machst du das über eine StoredProc

oder versuch mal den String

"SELECT FROM A; SELECT FROM B; SELECT FROM C"

abzusetzen, manche Treiber können dir dann 3 Resultsets geben


----------



## Schreihalz (11. Apr 2005)

es ist eine Maschine mit 2 Hyperthreading Prozessoren (Also quasi 4 Prozessoren)


----------



## AlArenal (11. Apr 2005)

Schreihalz hat gesagt.:
			
		

> es ist eine Maschine mit 2 Hyperthreading Prozessoren (Also quasi 4 Prozessoren)



Die aber nur über einen Bus auf den gemeinsamen RAM zugreifen


----------



## Bleiglanz (11. Apr 2005)

Schreihalz hat gesagt.:
			
		

> es ist eine Maschine mit 2 Hyperthreading Prozessoren (Also quasi 4 Prozessoren)



lies lieber mal nach, ob deine JVM das überhaupt ausnutzt (ist nicht immer der Fall)!

ausserdem hat das Ding wahrscheinlich nur eine Netzwerkkarte? Bei langsamer IO könntest du aber tatsächlich "schneller" sein, wenn du die Resultsets parallel von der DB holst, aber bei läppischen 1500 Sätzen würde mich das wundern

-> schon einen Vergleich mit der Single-Threaded Version gemacht? 

dabei fällt mir noch was auf


```
while(rs.next()){

     objekt = new ValueObject(rs); // aufwändige Objekterzeugung

}
```
ist ungünstig, weil das resultset länger offen ist als unbedingt nötig, ausserdem soll man das rs nicht herumreichen; für den server ist es besser, wenn man so schnell wie möglich durch-iteriert

würde eher das ValueObject vom sql entkoppeln und mir vorher über Metadaten die indizes holen (wie oben schon angeregt), der Konstruktor soll nur noch triviales Kopieren der Werte vornehmen


```
int wert1_index = ...// über resultsetmetadata den index der spalte holen
int wert2_index = ...// usw.


while(rs.next()){
 
     objekt = new ValueObject(rs.getString(wert1_index), rs.getInt(wert2_index), ...); // einfacher Wertkonstruktor

}
```


----------



## Schreihalz (11. Apr 2005)

Ok, das mit den Indizies habe ich gemacht, das hat etwas gebracht.. immerhin.

Das interessanteste ist jetzt ob die JVM das Multithreading mitmacht... Es ist tatsächlich so, das ich hier euf meinem Laptop entwickle und die Zeiten unterscheiden sich vom grossen Rechner kaum. Man muss wohl an der stelle JVM was unternehmen oder? Aber kann da was machen, verändern, einstellen oder muss man eine komplett andere(bestimmte) JVM aufsetzen?


----------



## AlArenal (11. Apr 2005)

Mein Kenntnisstand:

Die Threads in Java haben derzeit mit den Threads im jeweiligen OS nichts zu tun. Habe ich 30 parallele Threads in einer Java-Anwendung, ist diese auf System-Ebene nur als ein Prozess vertreten. Erst Java 6 soll dahingehend Abhilfe schaffen.

Mit paralleleln Threads ist in diesem Fall also keine wirkliche Performance-Steigerung möglich. Der erhöhte Aufwand für Synchronisation und Scheduling dürfte die Gesamtperformance der Anwendung eher verringern.


----------



## Bleiglanz (11. Apr 2005)

irgendwas passt wohl nicht, nimm mal -Xprof oder einen Profiler zur Hand

2 sekunden für 1500 Datensätze ist auf jeden fall nicht normal, selbst wenn du für jeden ein kleines (oder grosses) new aufrufst!

die bremse ist wahrscheinlich woanders


----------



## Schreihalz (11. Apr 2005)

Bleiglanz hat gesagt.:
			
		

> die bremse ist wahrscheinlich woanders



Zur Zeit bin ich hier echt 'ne Bremse 
Aber dat wird schon.. erstma ne runde rumprofilen


----------

