# große anzahl an inserts bei oracle db unperformant



## rambozola (14. Okt 2005)

hi leute,

ich hab ne datei die umfasst rund 61.000 zeilen.

diese muss ich im java-programm auslesen und jede zeile noch zerlegen und stringoperationen durchführen.

die ergebnisse soll ich dann noch in 3 oracle tabellen einfügen.

sagen wir mal für jede zeile hab ich im mittel 15 operationen...dann hab ich 640.500 inserts zu bewerkstelligen.

hatte das ganze mit executeQuery im jdbc (thin) propiert....hatte dann ausgerechnet das ich rund 135 h bräuchte bis alles eingetragen ist  :lol: 

dann hab ich gelesen das man noch ne batchverarbeitung machen kann (--> executeBatch() )...hab das implementiert aber mein rechner ist immer noch zu langsam und wird nich fertig.

habt ihr ne idee wie ich solch große datenmengen viel performanter in meiner oracle db unterbringen kann?

gibts da ne andere/bessere möglichkeit?

ich benutze jdk 5 und oracle 9.

vielen dank für eure antwort!


----------



## Bleiglanz (15. Okt 2005)

a) mit einer stored Procedure

b) 135 Stunden? schaffst du wirklich nur einen Insert pro Sekunde??

c) viele Indexe auf den betroffenen Tabellen? Constraints?

d) verwendest du prepared Statements?

e) du baust doch hoffentlich die Insert-Strings nicht mit + zusammen?


----------



## rambozola (15. Okt 2005)

zunächst einmal danke@Bleiglanz

zu a) was ist eine stored procedure

c) indexe dürfte ich nicht verwenden...die 4 tabellen sind nur durch diverse PS-FS-Beziehungen miteinander verknüpft 

d) bei meiner batchverarbeitung gehe ich folgender maßen vor.
    s ist ein statement.    ich adde die inserts mit s.addBatch(updateString); und führe dann s.executeBatch();
	  und s.clearBatch(); aus bevor ich den batch für eine andere tabelle wieder neu befülle.

e) nee, ausser das + um die notwendigen variablen zu nehmen baue ich die strings nicht mit + zusammen

hier mal ein bisserl code von mir damit du das ganze siehst

```
private static void redeTexteEinlesen() {
		
		String gesamtInhaltDerDatei = allesAusDateiAuslesen();
		ora.batchInitialisieren();
						
				//try{
				//in tokens = sätze zerlegen
				//StringTokenizer dateiTokens = new StringTokenizer(gesamtInhaltDerDatei,".");
		String anfrageString = "";
				while(gesamtInhaltDerDatei.indexOf('<')!= -1){
					
					//tag auslesen
					int tagAnfang = gesamtInhaltDerDatei.indexOf('<');
					int tagEnde = gesamtInhaltDerDatei.indexOf('>');
					String taginhalt = gesamtInhaltDerDatei.substring(tagAnfang+1, tagEnde);
					//System.out.println("taginhalt= "+taginhalt);
					
					//tag aus satz löschen
					StringBuffer sb = new StringBuffer(gesamtInhaltDerDatei);
					gesamtInhaltDerDatei = sb.delete(0, tagEnde+1).toString();
					//System.out.println("gesamtInhaltDerDatei ohne tag = "+gesamtInhaltDerDatei);
					
					String satzEinerRede = null;
					//wenn ende der datei nicht erreicht
					if(gesamtInhaltDerDatei.indexOf('<')!= -1){
						
					
//					satz = von stelle 0 bis index of '<'
					satzEinerRede = gesamtInhaltDerDatei.substring(0, gesamtInhaltDerDatei.indexOf('<'));
					//System.out.println("satzEinerRede = "+satzEinerRede);
					}
					//dateiende erreicht
					else{
						satzEinerRede = gesamtInhaltDerDatei;
						gesamtInhaltDerDatei = "";
					}
					
					//eingelesenen satz rauslöschen
					sb = new StringBuffer(gesamtInhaltDerDatei);
					tagAnfang = gesamtInhaltDerDatei.indexOf('<');
					if(tagAnfang != -1){
					gesamtInhaltDerDatei = sb.delete(0, tagAnfang).toString();
					//System.out.println("gesamt ohne satz = "+gesamtInhaltDerDatei);
					}
					
				
				StringTokenizer tagStringTokenizer = new StringTokenizer(taginhalt,":");
				String nrDerRede = tagStringTokenizer.nextToken();
				//System.out.println("nrDerRede = "+nrDerRede);
				String satzNr = tagStringTokenizer.nextToken();
				//System.out.println("satzNr = "+satzNr);
				String praesident = tagStringTokenizer.nextToken();
				//System.out.println("praesident = "+praesident);
				
				StringBuffer satz = new StringBuffer(satzEinerRede);
				
				//tag aus satz rausschneiden
				//satz.delete(0, tagEnde+1);
				
				//System.out.println("satz vor hochkommabearbeiten= "+satz.toString());
				
				//anstatt ' muss '' im satz stehen
				satz = satzHochKommataBearbeiten(satz);
				//System.out.println("satz nach hochkommabearbeiten= "+satz.toString());
				
				//vectoren für spätere behandlung bei VOLLINDEX befüllen
				nrDerRedeVector.addElement(nrDerRede);
				nrDesSatzesVector.addElement(satzNr);
				satzVector.addElement(satz);
				
				
				
				anfrageString = " insert into REDE values("+nrDerRede+","+satzNr+","
				+"'"+praesident+"','"+satz+"')";
				System.out.println("anfrageString = "+anfrageString);
				ora.batchFuellen(anfrageString);			
				}
				
				//ora.sqlAnfrageAnDBSchicken(anfrageString);
				
				
					ora.executeBatch();
									
				
				//inDateiFuerSchnelleresInsertSchreiben(anfrageString);
				
			//} 
			  /*catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}*/
			
		
		
		

	}
```

Der angegebene Code betrifft nur eine von den 4 tabellen.bei den anderen ist das ganze noch komplizierte da die eingelesenen texte auf sonderzeichen überprüft u bearbeitet werden müssen und danach zerlegt werden.

ich poste dir auch mal meine sql-klasse

ora ist eine instanz meiner sql-klasse


```
.....
Statement s = null;

.....
public void batchInitialisieren() {
		try {
			conORACLE.setAutoCommit(false);
			if (conORACLE != null) {
				s = conORACLE.createStatement();
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void batchFuellen(String updateString) {
		try {
			s.addBatch(updateString);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/**
	 * batch wird übertragen und geleert -->clear
	 *
	 */	
	public void executeBatch(){
		
	try
	{	
	  
	  s.executeBatch();
	  s.clearBatch();
			
	}
	catch ( BatchUpdateException e ) { e.printStackTrace();}
	catch ( SQLException e ) {e.printStackTrace(); }
	}
```

Was meinst du/ihr dazu?  :?


----------



## rambozola (15. Okt 2005)

eine sache müsste ich noch ergänzen...ich committe die transaktion erst am ende meines programmes...soweit komme ich allerdings nicht, da vorher der java-heap-space nicht reicht u ich eine oom-exception bekomme.

die parameter -Xms und -Xmx habe ich bereits verwendet.


----------



## stev.glasow (15. Okt 2005)

so was 


> anfrageString = " insert into REDE values("+nrDerRede+","+satzNr+","
> +"'"+praesident+"','"+satz+"')";


ist schon mal Schied:


> e) du baust doch hoffentlich die Insert-Strings nicht mit + zusammen?



weiter hab ich nicht geguckt


----------



## rambozola (15. Okt 2005)

ok stevg wie gestalte ich das ganze performanter wenn nicht mit +?

muss ich da ein char[] oder ähnliches machen oder gibts gar ne Stringmethode die ich nicht kenne?


----------



## stev.glasow (15. Okt 2005)

Versuch mal StringBuffer bzw. StringBuilder 
[edit]
Noch was ganz böses: String gesamtInhaltDerDatei = allesAusDateiAuslesen(); 
Die Datei ist bestimmt recht groß, und mit der Zeile haust du die ganze Datei in den Arbeitsspeicherm kannst du das nicht evt. zeilenweise oder ähnliches einlesen ?
[edit]
Dass bei jedem Durchgang was vom gesamtInhaltDerDatei-String abgeschnitten und wieder in die Varibale gespeichert wird ist auch gar nicht gut.
sry, aber keine Wunder das dir das um die Ohren fliegt, ich weiß der Spruch hilft nicht viel aber deine Stringsachen musst doch nochmal überdenken  :?


----------



## DP (15. Okt 2005)

StringBuffer s = new StringBuffer("insert into REDE values(");
s.append(nrDerRede);
s.append(",");
s.append(satzNr);
.
.
.
.
dann ...execute(s.toString);


----------



## rambozola (15. Okt 2005)

@stevg die datei ist 8 mb groß u ich denke ich muss sie komplett einlesen da ich sie ja in mehreren sequentiell aufeinanderfolgenden methoden (lt. aufgabenstellung) benötige.

an euch beide: jepp stringbuffer is ne gute idee ich propier das jetzt mal aus.


----------



## stev.glasow (15. Okt 2005)

hab noch mal was editiert(siehe oben), ersters und letzters würde ich auf jeden fall nochmal bedenken, das letzte auch bei einer 8 MB Datei.


----------



## rambozola (15. Okt 2005)

ok stevg: wie gehts besser?

da ich bisher eher miniprogramme geschrieben habe hatte ich noch nie das problem mit großen zeichenmengen.


----------



## stev.glasow (15. Okt 2005)

gute Frage, weiß ich jetzt auch nicht ausm stehgreif.
Würde jetzt nen Scanner mit "<" als Delimiter auf dem FileinputStream laufen lassen.


----------



## bronks (16. Okt 2005)

rambozola hat gesagt.:
			
		

> @stevg die datei ist 8 mb groß u ich denke ich muss sie komplett einlesen da ich sie ja in mehreren sequentiell aufeinanderfolgenden methoden (lt. aufgabenstellung) benötige ...


Ich habe mir auch mal eingebildet, daß man die Daten so in die DB reinbekommt, aber das kann man echt vergessen.

Ob man den String mit + oder - oder sonstwas zusammenbastelt ist eigentlich total egal, denn warten muß man immer auf die DB. 

60000 Zeilen im Batch überfordern Java um einiges und auch wenn es nur 1000 Zeilen sind, dann wäre es wegen dem Verwaltungsaufwand deutlich langsamer, als einzelne Executes. Das ganze in einer Transaktion ist auch nicht ratsam, weil die DB nicht die Geduld aufbringen wird in einer Transaktion so lange zuzuhören und Dir die Transaktion kickt. 

Eine 8 MB-Datei im Speicher zu halten und mehrere male durchzunudeln ist auch kein Spaß für die JVM und bringt keinen Performancevorteil.

Gestalte das Programm so, daß die Datei zeilenweise abgearbeitet wird und das Füllen der 4 Tabellen je Dateizeile in einer Transaktion und im Batch abgearbeitet wird. Wohl gemerkt: Je Zeile eine Transaktion und ein Batchaufruf. Bei den schnellsten Produktivsystemen, die ich kenne würde ich mit <400 Transkationen je Minute rechnen.

Zum Einspielen von Daten bieten die DBs bessere Werkzeuge, welche man auch verwenden sollte.


----------



## Bleiglanz (17. Okt 2005)

```
redeTexteEinlesen() {
```
ist hoffnungslos inperformant

die whileschleife und darin dann ein teilstück aus einem String löschen mit delete?

ist das xml?


----------



## Guest (18. Okt 2005)

> @bronks Ob man den String mit + oder - oder sonstwas zusammenbastelt ist eigentlich total egal, denn warten muß man immer auf die DB.



Einspruch!

Aus meiner Sicht sollte unbedingt das INSERT ...VALUES(Daten)...  durch ein *PreparedStatement* (=INSERT ... VALUES(?, ?) ersetzt werden. 

1. spart man sich ein paar String(Buffer) Manipulationen ein (pro Transaktion, also x INSERT's)
2. muss die Datenbank den Befehl nur 1x interpretieren / bzw. hat einen kürzen String zu interpretieren (je nach DB)
3. hat eben diese Optimierung von 2., unschöne Nebenwirkungen bei INSERT's ohne Parameter (macht den Statement Cache zu - kann in's Gegenteil umschlagen)

LG Robert


----------



## rambozola (18. Okt 2005)

vielen dank leute für eure hilfe...ich habs jetzt so hinbekommen mit der batchverarbeitung und auto-commit off (committet wird nur alle 1000 transaktionen) das ich ca 1000 transaktionen pro minute geschafft habe (mit hoher prio im taskmanager).
mein proggie war also mit den ca 61000 zeilen die mehrere inserts ergaben nach bissi über ner h fertig.

das ist io.

thanks nochmal.
thorsten


----------

