# Performance enorm langsam



## MalakEkan (6. Mrz 2007)

Also es geht um folgendes. Ich habe eine Textdatei die ich zeile für zeile auslese. Je nachdem welche Wörter am Anfang einer Zeile steht soll mein Programm mir diese Zeile in eine Datenbank schreiben. Also wenn die Zeile mit xyz anfängt so soll er mir diese komplette Zeile in die Datenbank schreiben. 
Ich habe es bis jetzt so gelöst



```
BufferedReader datei= new BufferedReader(new FileReader("[b]PFAD ZUR DATEI[/b]"));
		String sZeile, query;
		sZeile=datei.readLine();



		/** Daten von der Textdatei einlesen und bearbeiten */
		while(sZeile != null){
			if (sZeile.startsWith("dn: ")){

				query="INSERT INTO Temp(dn) VALUES ('"+ sZeile.substring(4) + "');";
				stmt.executeUpdate(query);
				ID = sZeile.substring(4);
			}
			else 
				if (sZeile.startsWith("displayName:")){
					query="UPDATE Temp SET displayname = '" + sZeile.substring(12) + "' WHERE dn = '" + ID + "';";
					stmt.executeUpdate(query);
				}
				else
					if(sZeile.startsWith("objectClass:")){

						if((sZeile.startsWith("user", 13)) || sZeile.startsWith("computer", 13)){
							query="UPDATE Temp SET typ = '" + sZeile.substring(12) + "' WHERE dn = '" + ID + "';";
							stmt.executeUpdate(query);
						}
						
					}
			sZeile=datei.readLine();
		}
```

Das ganze klappt soweit wirklich gut. Nur dauert es enorm lange bis die Daten eingelesen wurden. 
Vielleicht sollte ich noch erwähnen das die Textdatei, die ich auslese so an die 8,3 MB groß ist. Wäre für jede Idee dankbar.


----------



## SlaterB (6. Mrz 2007)

nun, wie soll man tausende Updates schneller machen?
die dürften sicherlich so lange dauern,
es sei denn du hast irgendwelche anderen Update-Mengen zum Zeitvergleich?

miss doch mal die Durchschnittszeit pro Update, 
wirds sie vielleicht im Verlauf der Datei immer höher?

--------

falls mehrmals die gleichen Zeilen in der DB geändert werden,
dann ist es natürlich keine Frage, dass es sparsamer wäre, erst in Java den endgültigen Wert zu bestimmen und diesen dann einmalig zu speichern


----------



## EgonOlsen (6. Mrz 2007)

Kannst es mal mit PreparedStatement versuchen. Also die PreparedStatements vor der Schleife generieren und in der Schleife nur noch füllen und ausführen. Je nach Datenbank bringt das durchaus was.


----------



## Guest (11. Mrz 2007)

...und zusätzlich noch statt executeUpdate() in der Schleife nur addBatch()
und nach dem Verlassen der Schleife executeBatch(). Sind es zu viele Daten, 
dann z.B. alle 1000 Records ein executeBatch() ausführen.


----------



## DP (11. Mrz 2007)

und nimm einen stringbuffer.append anstatt das ewige stirng + " " + string etc., das frisst performance ohne ende


----------



## bronks (12. Mrz 2007)

DP hat gesagt.:
			
		

> und nimm einen stringbuffer.append anstatt das ewige stirng + " " + string etc., das frisst performance ohne ende


Das glaube ich nicht und würde es gerne bewiesen haben. O.g. Code läßt sich durch die Verwendung eines Stringbuffers sicher nicht schneller machen. Das + dürfte für diesen Einsatzzweck das einzige wahre sein.


----------



## Caffè Latte (12. Mrz 2007)

Hi,



			
				bronks hat gesagt.:
			
		

> DP hat gesagt.:
> 
> 
> 
> ...



Der Beweis ist schon tausendmal erbracht worden. Da Strings immutable sind, wird bei jeder Konkatenation ein neues Stringobjekt erzeugt, dies ist extrem inperformant.

Im obigen Fall würde ich trotzdem zu PreparedStatements raten ...


----------



## bronks (12. Mrz 2007)

Caffè Latte hat gesagt.:
			
		

> ... Der Beweis ist schon tausendmal erbracht worden. Da Strings immutable sind, wird bei jeder Konkatenation ein neues Stringobjekt erzeugt, dies ist extrem inperformant ...


Weil es DP gerade hier erwähnt hat, habe ich ein vergleichbares Beispiel zu o.g. durch den Profiler gejagt. 

Den Stringbuffer müsste man m.E. in o.g. Beispiel für jeden Satz leeren oder ein neues StringBufferObjekt erstellen. Gibt es sonst eine andere Möglichkeit einen kompletten SQL-String zusamenzubasteln?

@MalakEkan:
Wenn Du das PreparedStatement getestet hast, dann berichte bitte, ob es Vorteile gebracht hat.


----------



## DP (12. Mrz 2007)

ich habe es mehr generell gemeint. in einer schleife ist ein pstmt sicherlich performanter.


----------



## robertpic71 (12. Mrz 2007)

Neben der Verwendung von PreparedStatements noch andere Anregungen:

Wie schaut es mit dem Commit aus? Ist Autocommit eingeschaltet? Sowohl Autocommit ein (je DB-Update noch ein 2. für das Commit), als auch Autocommit aus (große Transaktionen werden von der DB zwischengespeichert/gesperrt) wirken sich negativ auf die Performance aus.

Meine Empfehlung:
Autocommit aus. COMMIT nach allen 500 Updates (ev. andere Werte testen, hier können je nach Datenmenge/Datenbank, andere Werte performanter sein) und ein COMMIT am Ende!

Das gleiche gilt hier auch für das Batch-Update. Ein riesiges Batch-Update macht eher Probleme (Bufferüberlauf) als mehere Updategruppen mit jeweils (z.B. 500 oder 1000) Statements.

Das Batch-Update würde ich erst machen, wenn das andere (PreparedStatement + Commit-Gruppen) nicht genung bringt.


----------



## SlaterB (12. Mrz 2007)

bronks hat gesagt.:
			
		

> Den Stringbuffer müsste man m.E. in o.g. Beispiel für jeden Satz leeren oder ein neues StringBufferObjekt erstellen. Gibt es sonst eine andere Möglichkeit einen kompletten SQL-String zusamenzubasteln?


leeren wäre gut (aber vorher testen ob nicht langsamer als neu erstellen  ),

neu erstellen ist aber auch nicht schlimm
wann immer zwei Strings mit + verbunden werden, wird intern eh ein StringBuffer erstellt,
durch manuelles Nachbauen wird das nicht langsamer,

aber schon ab drei Strings und zwei + ist dieser eine StringBuffer besser als die zwei, die intern erstellt werden würden


----------



## bronks (12. Mrz 2007)

SlaterB hat gesagt.:
			
		

> ...
> leeren wäre gut (aber vorher testen ob nicht langsamer als neu erstellen  ),
> 
> neu erstellen ist aber auch nicht schlimm
> ...


Du irrst Dich! 

```
public class ZeitmessungBufferAppend {
    
    String s;
    StringBuffer sb = new StringBuffer();
    String s1 = "jklö";
    int it = 1000000;
    /** Creates a new instance of ZeitmessungBufferAppend */
    public ZeitmessungBufferAppend() {
        System.out.println("X");
        
        long m1 = System.currentTimeMillis();
        for(int i=0; i<it; i++){
            sb.delete(0, sb.length());
            sb.append("asdf" + i + "fdsa" + s1 + "noch etwas" + s1 + i +  "X" + "x");
        }
        
        
        long m2 = System.currentTimeMillis();
        for(int i=0; i<it; i++){
            sb = new StringBuffer();
            sb.append("asdf" + i + "fdsa" + s1 + "noch etwas" + s1 + i + "X" + "x");
        }
        
        long m3 = System.currentTimeMillis();

        for(int i=0; i<it; i++){
            s=null;
            s=("asdf" + i + "fdsa" + s1 + "noch etwas" + s1 + i + "X" + "x");
        }
        
        long m4 = System.currentTimeMillis();
        System.out.println(m2-m1);
        System.out.println(m3-m2);
        System.out.println(m4-m3);
    }

    public static void main(String[] args){
        ZeitmessungBufferAppend test = new ZeitmessungBufferAppend();
    }
}
```


----------



## Murray (12. Mrz 2007)

@bronks: Einen StringBuffer zu erzeugen, um ihn dann nicht zu verwenden, ist aber sicher nicht Sinn der Sache.

Und in solchen Situationen, in denen Thread-Synchronisierung nicht benötigt wird, sollte man ohnehin lieber den 
StringBuilder verwenden (ab JDK 1.5)


----------



## DP (12. Mrz 2007)

bronks hat gesagt.:
			
		

> ```
> sb.append("asdf" + i + "fdsa" + s1 + "noch etwas" + s1 + i +  "X" + "x");
> ```



ist doch der gleiche humbug nur im stringbuffer


----------



## The_S (12. Mrz 2007)

@bronks

1. siehe Murray
2. siehe DP
3. es sagen dir so viele Leute die Ahnung von der Materie haben, warum glaubst dus nicht einfach?


----------



## Wildcard (12. Mrz 2007)

Ist zwar schon oft genug gesagt worden, aber ich schließe mich gerne an das 

```
sb.append("asdf" + i + "fdsa" + s1 + "noch etwas" + s1 + i + "X" + "x");
```
völlig daneben ist  :lol: 
Wo wir aber dabei sind:
Solange keine synchronisierung erforderlich ist sollte man den StringBuilder statt dem Buffer nehmen und in beiden Fällen ist es sehr wichtig eine sinnvolle initiale Kapazität festzulegen (wann immer möglich).


----------



## bronks (12. Mrz 2007)

Murray hat gesagt.:
			
		

> @bronks: Einen StringBuffer zu erzeugen, um ihn dann nicht zu verwenden, ist aber sicher nicht Sinn der Sache ...


OK OK ... ...!

Ab morgen schütte ich mir keinen Eierlikör mehr in den Frühstückskaffee.  

Nach dem der Rausch ausgeschlafen ist sieht die Welt gleich ganz anders aus:

```
public class ZeitmessungBufferAppend {
    
    String s;
    StringBuffer sb = new StringBuffer();
    String s1 = "jklö";
    int it = 1000000;
    /** Creates a new instance of ZeitmessungBufferAppend */
    public ZeitmessungBufferAppend() {
        System.out.println("X");
        
        long m1 = System.currentTimeMillis();
        for(int i=0; i<it; i++){
            sb.delete(0, sb.length());
            sb.append("asdf");
            sb.append(i);
            sb.append("fdsa");
            sb.append(s1);
            sb.append("noch etwas");
            sb.append(s1);
            sb.append(i);
            sb.append("X");
            sb.append("x");
        }
        
        
        long m2 = System.currentTimeMillis();
        for(int i=0; i<it; i++){
            sb = new StringBuffer();
            sb.delete(0, sb.length());
            sb.append("asdf");
            sb.append(i);
            sb.append("fdsa");
            sb.append(s1);
            sb.append("noch etwas");
            sb.append(s1);
            sb.append(i);
            sb.append("X");
            sb.append("x");
        }
        
        long m3 = System.currentTimeMillis();

        for(int i=0; i<it; i++){
            s=null;
            s=("asdf" + i + "fdsa" + s1 + "noch etwas" + s1 + i + "X" + "x");
        }
        
        long m4 = System.currentTimeMillis();
        System.out.println(m2-m1);
        System.out.println(m3-m2);
        System.out.println(m4-m3);
    }

    public static void main(String[] args){
        ZeitmessungBufferAppend test = new ZeitmessungBufferAppend();
    }
}
```


----------



## MalakEkan (13. Mrz 2007)

Also ich wollte es jetzt mit dem PreparedStatement versuchen. Allerdings bekomme ich jetzt immer einen NullPoitnerException. 
HIer der Code:


```
BufferedReader datei= new BufferedReader(new FileReader(PFAD ZUR DATEI));
		
	      String sZeile; 
	      sZeile=datei.readLine(); 

	      
	      PreparedStatement psInsert=con.prepareStatement("INSERT INTO Temp(dn, displayname, typ) VALUES ('?', '?', '?');");
	      /** Daten von der Textdatei einlesen und bearbeiten */ 
	      while(sZeile != null){ 
	         if (sZeile.startsWith("dn: ")){ 

	            
	        	 String string=sZeile.substring(4);
	        	 psInsert.setString(1, string);
	        	  
	         } 
	         else 
	            if (sZeile.startsWith("displayName:")){ 
	                
	                 psInsert.setString(2, sZeile.substring(12));
	            } 
	            else 
	               if(sZeile.startsWith("objectClass:")){ 

	                  if((sZeile.startsWith("user", 13)) || sZeile.startsWith("computer", 13)){ 
	                     	  psInsert.setString(3, sZeile.substring(12));
	                	  psInsert.executeUpdate();
	                  } 
	                   
	               } 
	         sZeile=datei.readLine(); 
	      }
```

Laut Eclipse ist der Fehler (in diesem Codeabschnitt) in Zeile 14. Aber das ist mir unbegreiflich. Der String ist richtig und die Syntax vom PreparedStatement auch. Also wo ist da der Fehler?


----------



## SlaterB (13. Mrz 2007)

was soll dieser Quatsch-Code?
schreibe 

```
PreparedStatement psInsert=con.prepareStatement(
   "INSERT INTO Temp(dn, displayname, typ) VALUES ('?', '?', '?');");
psInsert.setString(1, "test");
```

entweder das geht oder nicht,
wenn es nicht geht dann darfst du dich weiter wundern,

wenn doch, dann ist alles klar und es ist ein Fehler in deinem Code, der nur noch gefunden werden muss

der nächste Test wäre z.B.

```
PreparedStatement psInsert=con.prepareStatement(
  "INSERT INTO Temp(dn, displayname, typ) VALUES ('?', '?', '?');");
         while(sZeile != null){
            if (sZeile.startsWith("dn: ")){

               psInsert.setString(1, "test");
               
            }
            sZeile=datei.readLine();
         }
```

geht das, ja oder nein?

und so gehts Schritt für Schritt voran,
ein Fehler im fertigen Programm NICHT zu finden ist keine Kunst..


------------

und probiere mal 
"VALUES (?, ?, ?);"
statt
"VALUES ('?', '?', '?');"


----------



## DP (13. Mrz 2007)

und wenn dann 


```
INSERT INTO Temp(dn, displayname, typ) VALUES (?, ?, ?);");
```


----------



## MalakEkan (13. Mrz 2007)

Danke habs gemerkt. Aber die Performance ist immer noch so lahm. So wirklich was nutzen tut das PreparedStatement auch nichts  :?


----------



## Gast (13. Mrz 2007)

was für eine DB benutzt du eigentlich?


----------



## MalakEkan (13. Mrz 2007)

Ich nutze eine Access Datenbank.


----------



## robertpic71 (13. Mrz 2007)

1.) Gibt es jetzt noch die Updatebefehle oder nur noch das Insert? 

2.) Hast du einen Index (oder mehrere CREATE INDEX...) über Datei liegen?

3.) Autocommit?

Teste mal:

Vor der Schleife:
con.setAutoCommit(false);
int sqlCounter = 0;

In der SChleife beim Update:

```
if (sqlCounter++ > 300) {
    con.commit();
    sqlCounter = 0;
}
```

nach der Schleife:

```
if (sqlCounter++ > 0) {
    con.commit();
}
```

Eventuell liegt das Problem auch an der Ewartungshaltung? Welche Datenmengen kommen da in die Datenbank (Sätze/Größe)?

Die nächste Steigerung wäre ein Batch-Update.

Statt

psInsert.executeUpdate(); 

kommt

psInsert.addBatch();


Und in die 2 If's von oben kommt vor das Commit (Beispiel ohne Statusabfrage):
psInsert.executeBatch();


----------



## MalakEkan (14. Mrz 2007)

Danke für eure Hilfe. 
Die Perfomance ist viel besser als zum Anfang.

Jetzt braucht das Programm nur noch 5 Sekunden für 215968 Textzeilen zum einlesen. 

Ich denke das ist eine akzeptable Zeit oder?


----------



## SlaterB (14. Mrz 2007)

interessant wäre da die vorherige Zeit und welche Verfahren letztlich etwas beigetragen haben
(falls das nicht schon irgendwo steht, habe nicht alles gelesen)


----------



## MalakEkan (14. Mrz 2007)

Die Zeit habe ich leider nicht von Anfang an gemessen. 
Ich kann nur soviel sagen das es am Anfang solange gedauert hat das man sich in aller Ruhe einen Kaffee hätte holen können. 

Ich vermute mal es hat einiges gebracht das ich diese Updates nicht mehr jedesmal durchführe.


----------

