Schrift
Wiki:Tipp zum Debugging: use Data::Dumper; local $Data::Dumper::Useqq = 1; print Dumper \@var;
[thread]3758[/thread]

MySQL Verständnisfrage: 100 Datensätze in eiem Schwung eintragen (Seite 2)



<< |< 1 2 >| >> 18 Einträge, 2 Seiten
olruebe01
 2007-01-05 15:59
#35048 #35048
User since
2006-01-19
192 Artikel
BenutzerIn
[default_avatar]
Hallo Nepos, hallo pq,

danke für Eure hilfe.
Ich setze jetzt die von Euch vorgeschlagene Version um. Also ein prepare und dann jedesmal in der Schleife ein Execute.

Nun habe ich ein neues PRoblem:
Ich lese per Script die csv-Dateien Zeilenweise ein, ändere diese so wie ich es brauche und trage sie dann per Execute ein. Den prepare habe ich vor der Schleife gesetzt...

Vorteil: Der benötigte Arbeitsspeicher ist jetzt 99% geringer. Juhu...

Aber: In der DB sind 3,6 Mio Zeilen. Nun habe ich eine aktualisierte CSV bekommen, lösche alle alten (betroffenen) Zeilen, um die neuen Zeilen einzulesen. So als Richtwert: Bei einer Aktualisierung gibt es etwa 300.000 neue Zeilen (mal mehr, mal weniger).
Das Script erzeugt einen I/O-Wait von 80-95% und einen LoadAverage von 6-8 (normal habe ich etwa 0,5). Also denke ich "das kann es nicht sein" und kille dieses Script. Aber: Die MySQL-Befehle werden noch STUNDEN weiter ausgeführt und der I/O WAIT bleibt hoch.

Im Klartext: Nachdem ich das Script gekillt habe, werden noch STUNDEN Daten in die DB geschrieben und der I/O-Wait bleibt hoch. Mein Gedanke: Kann es sein, dass das Script die SQL-Befehle schneller ausgibt, als diese von Mysql abgefrühstückt werden können? Ist es also möglich, dass die ganzen MySQL-Befehle in einer Warteschlange landen und auch nach Beendung des Scripts weiter ausgeführt werden?

Wo kann ich nach Fehlern suchen, wie mache ich es besser?

Mal abgesehen von einem eventuellem Fehler habe ich 2 Gedanken:
Vielleicht ist die Tabelle einfach inzwischen so groß, dass es halt seine Zeit dauert???
Idee: Ich lege eine zweite identische Tabelle an, trage alle neuen Daten hier ein. Da die Tabelle leer ist, könnte das schneller gehen(??). Nachdem alle Daten drin sind, kopiere ich die Zeilen von einer Tabelle in die andere...

Idee2: Ich lese die CSV per Script ein, ändere die Werte wie ich sie brauche und schreibe die Datei wieder als CSV auf die Festplatte. Danach lade die Daten per LOAD INFILE in die DB.

Am liebsten würde ich natürlich einen Fehler entdecken und beseitigen :-)

Hier mal der relevante Code:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
### Zuerst wurden neue Daten gefunden, geladen und lokal gespeichert. Dies klappt (sehr schnell)

sub eintragen{   ### Wird bei jeder aktualisierten Datei aufgerufen...

$file = "produktdaten/$PartnerID.dat";
print "FILE: $PartnerID<BR>";


$dbh->do (qq~DELETE  FROM `Produktdaten` WHERE `PartnerID` = '$PartnerID'~); ### Alte Einträge dieses Partners löschen



my $sql = qq{insert into  Produktdaten(PartnerID, Bezeichnung,Kurzbeschreibung,Beschreibung,Preis,alterPreis,Waehrung,Keywords,Bild_kl,Bild_mittel,Bild_gross,Deeplink,gueltig_von,gueltig_bis,status,lieferzeit,Bedingungen,Hersteller,Brand,Partner_Kategorie, aktualisiert, laufendeNummer) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)};
my $stheintragen = $dbh->prepare( $sql );



open(FILE, "$file") or die "Could not open $file: $!\n";
while (defined (my $ZEILE = <FILE>)) { ### Zeilenweise neue Daten aus der CSV einlesen


......
Nun werden die Einzelnen Zeilen bearbeitet / angepasst. Z.B. Datum in Sekunden wandeln, Komma in Punkt wandeln, etc...
......



(undef, undef, $MerchantProductNumber, $Bezeichnung, $Preis, $alterPreis, $Waehrung, $update, $gueltig_von, $gueltig_bis, $kurzbeschreibung, $Beschreibung, $status, $Partner_Kategorie, undef, $Bild_kl, $Bild_mittel, $Bild_gross, $Hersteller, $Deeplink, undef, undef, $lieferzeit, $Bedingungen, $EAN) = split(/<SPLIT>/, $ZEILE);



if ($update =~ " "){   ### Zur Sicherheit. Einige Datumsangaben haben ein anderes Format
($datum, undef) = split(/ /, $update);
&sekunden;
$update = $sekunden;
}



if ($gueltig_von =~ " "){ ### Zur Sicherheit. Einige Datumsangaben haben ein anderes Format
($datum, undef) = split(/ /, $gueltig_von);
&sekunden;
$gueltig_von = $sekunden;
}



if ($gueltig_bis =~ " "){ ### Zur Sicherheit. Einige Datumsangaben haben ein anderes Format
($datum, undef) = split(/ /, $gueltig_bis);
&sekunden;
$gueltig_bis = $sekunden;
}



$laufendeNummer++;



$stheintragen->execute($PartnerID, $Bezeichnung,$kurzbeschreibung,$Beschreibung,$Preis,$alterPreis,$Waehrung,$Keywords,$Bild_kl,$Bild_mittel,$Bild_gross,$Deeplink,$gueltig_von,$gueltig_bis,$status,$lieferzeit,$Bedingungen,$Hersteller,$Brand, $Partner_Kategorie, $update, $ID)|| die "Database connection not made: $DBI::errstr";



} ## Ende der ZEILE



$stheintragen->finish();



} ## ENDE sub -> Ende der neuen Datei


Mache ich was falsch oder dauert das einfach? Wie kann ich verhindern, dass die ganzen SQL-Befehler in einer "Warteschleife" landen?
pq
 2007-01-05 18:07
#35049 #35049
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
also 100 datensätze und 300.000 ist schon ein unterschied.
hat die tabelle indizes? evtl. lohnt es sich, die indizes während des einfügens abzuschalten.

ach ja, und es lohnt sich, die zeilenlänge in skripten auf 80 zu begrenzen.

und: nein, du führst ein execute aus und wartest, bis es durch ist. deshalb
können die befehle nicht in einer "warteschleife" enden. du kriegst ja
auch mit, ob es klappt oder nicht. das kann mysql ja vorher nicht ahnen.\n\n

<!--EDIT|pq|1168013504-->
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
olruebe01
 2007-01-05 18:28
#35050 #35050
User since
2006-01-19
192 Artikel
BenutzerIn
[default_avatar]
Hallo pq: Ja, 100 und 300.000 ist ein Unterschied. 100 war ja nur mal so ein Wert, um das ganze GENERELL zu verstehen. Mit meinem letzten Beitrag hat sich das Thema aber von GENERELL in SPEZIELL gewandelt...

Ich verstehe aber nicht, warum weiter Daten in die DB geschrieben werden, wenn ich das Script gekillt habe???!?? Warum macht mysql dass dann noch, wenn es die Befehle doch von dem (schon gekillten) Sktipt bekommt?

Wie meinst Du das, "die Zeilenlänge auf 80 begrenzen"??? 80 Zeilen oder 80 Zeichen je Zeile?
Und was mache ich mit den Restlichen Daten??? Die brauche ich ja auch...

Wenn ich die Indizes abschalte, müssen diese doch nach den neuen Daten wieder neu erstellt werden? Dauert das dann nicht genau solange?
pq
 2007-01-05 19:40
#35051 #35051
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
[quote=olruebe01,05.01.2007, 17:28]Hallo pq: Ja, 100 und 300.000 ist ein Unterschied. 100 war ja nur mal so ein Wert, um das ganze GENERELL zu verstehen. Mit meinem letzten Beitrag hat sich das Thema aber von GENERELL in SPEZIELL gewandelt...[/quote]
naja, es ist nur so, dass es ein grosser unterschied ist und die
lösung davon abhängt. 100 datensätze sind prima für ein script,
aber 300.000 sind als direkter input für mysql per datei natürlich
viel effizienter. bei 100 merkst du den unterschied sicher kaum oder
gar nicht, bei 300.00 bestimmt.
Quote
Ich verstehe aber nicht, warum weiter Daten in die DB geschrieben werden, wenn ich das Script gekillt habe???!?? Warum macht mysql dass dann noch, wenn es die Befehle doch von dem (schon gekillten) Sktipt bekommt?

das weiss ich auch nicht. wie hast du denn das script gekillt?
was sagt "show processlist"?
Quote
Wie meinst Du das, "die Zeilenlänge auf 80 begrenzen"??? 80 Zeilen oder 80 Zeichen je Zeile?

80 zeichen pro zeile. "zeilenlänge" eben.
Quote
Und was mache ich mit den Restlichen Daten??? Die brauche ich ja auch...

ich meinte hier das script. deswegen sagte ich auch "in scripten".

Quote
Wenn ich die Indizes abschalte, müssen diese doch nach den neuen Daten wieder neu erstellt werden? Dauert das dann nicht genau solange?

das kommt wieder auf die menge der daten an, die du einfügst und die
schon drin sind. musst du ausprobieren.

wie ist die datenbank sonst so belastet, wie schnell ist der rechner,
wie viele felder hat die tabelle? 300.000 datensätze sollten jedenfalls
nicht mehrere stunden brauchen.
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
olruebe01
 2007-01-05 20:37
#35052 #35052
User since
2006-01-19
192 Artikel
BenutzerIn
[default_avatar]
Ich habe den Prozess via Putty gekillt. Hier erscheint das Script nicht mehr aber mehrere Mysql-Prozesse. Wenn ich einen hiervon kille, ist ganz mysql tot und ich muss(te) den Server rebooten. Allerdings habe ich das gemacht, weil ich mysql zu der Zeit nicht restarten konnte, weil ich den Befehl nicht kannte (Google ergab einen Befehl, der bei mir nicht funzte).

Die 80 Zeichen habe ich immer noch nicht verstanden. ICh habe eine csv-Datei, die im Schnitt 483 Zeichen pro Zeile hat. Mit einem Script lese ich die CSV zeilenweise ein und lasse die Daten in die DB eintragen. Wo soll ich da was auf 80 Zeichen begrenzen, wenn ich alle Daten brauchen?

Meine Tabelle hat 20 Zellen und wenn sie komplett befüllt ist etwa 3,6 Mio Zeilen (á durchscnittlich 483 Zeichen).
ICh habe das auf 2 Servern probiert:
Der eine hat 2,8 GHz und 500 MB RAM, der andere hat 3,2 GHz und 2 GB RAM. Macht auf beiden keinen Unterschied.

Meinst Du mit "direkter input für mysql per datei " die Funktion, die LOAD DATA INFILE?
olruebe01
 2007-01-06 04:02
#35053 #35053
User since
2006-01-19
192 Artikel
BenutzerIn
[default_avatar]
Problem gelöst!!!!

Das Problem ist folgendes: Bei jedem INSERT (oder dem späteren EXECUTE dazu) werden alle Indizes neu erstellt. Ich habe eine Tabelle mit 3,6 Mio Zeilen, die 3 Indizes hat. Ich weiß nicht, ob das wichtig ist aber einer davon ist ein Fulltext über 4 Zellen. Könnte vielleicht besonders groß sein??? Egal...

Wenn ich nun 300.000 neue Zeilen einfüge, werden also auch 300.000 mal alle Indizes überarbeitet. DAS DAUERT.

ICh dachte, dass die Indizes am ende des Scripts EIN MAL überarbeitet werden. Ist aber nicht so. PQ`s Ansatz war also vollkommen richtig. Dennoch dauert das erstellen der neuen Indizes etwas (und in dieser Zeit wäre die Tabelle ohne Indizes) und so mache ich es nun folgender maßen:
Ich erstelle eine TEMP-Tabelle. Eine exakte Kopie der orioginalen Tabelle, nur ohne Indizes.
Hier trage ich nun alle(!) Daten ein. Das dauert bei 3,6 Mio Daten jetzt noch etwa 25 - 30 Minuten. Danach erstelle ich alle Indizes, was wieder etwa 10-15 Minuten dauert. Nun lösche ich die originale Tabelle (bis zu diesem Zeitpunkt war die originale Tabelle noch für alles zu gebrauchen - mit Indizes!!) und benenne die Temp-Tabelle in den Originalnamen um. Fertig.
Vorteil: Für den Nutzer steht die originale Tabelle auch während des Updates voll zur Verfügung.

Das Ganze dauert nun weniger als eine Stunde, was vorher einmal über 36 Stunden durchlief. Ich halte eine Stunde bei 3,6 Mio Zeilen für ok.

Lieber wäre mir gewesen, nur die NEUEN Daten in eine Temptabelle zu schreiben und die gesamtem Daten aus der Temp-Tabelle in einem rutsch in die originale Tabelle zu kopieren...
Code: (dl )
1
2
INSERT INTO Tabelle
SELECT * from Temp-Tabelle


..aber irgendwie habe ich das (noch) nicht hinbekommen. Es wurde immer nur EINE Zeile übernommen...

Damit werde ich mich auseinenader Setzen, wenn ich mal ausgeschlafen habe.

VIelen Dank mal wieder für die Hilfe!! Irgendwann schicke ich euch allen einen Kasten Bier :-)
renee
 2007-01-06 12:54
#35054 #35054
User since
2003-08-04
14371 Artikel
ModeratorIn
[Homepage] [default_avatar]
[quote=olruebe01,05.01.2007, 19:37][...]
Die 80 Zeichen habe ich immer noch nicht verstanden. ICh habe eine csv-Datei, die im Schnitt 483 Zeichen pro Zeile hat. Mit einem Script lese ich die CSV zeilenweise ein und lasse die Daten in die DB eintragen. Wo soll ich da was auf 80 Zeichen begrenzen, wenn ich alle Daten brauchen?

[...][/quote]
pq meinte damit, dass Du keine übermäßig langen Zeilen in einem Skript haben solltest - wegen der Übersichtlichkeit.

Vergleiche:
Code: (dl )
1
2
 if(defined $var and $var =~ /dies ist ein langer RegEx/ and $var =~ /noch ein laengerer RegEx mit vielen Worten/ and $var =~ /test/){
}
mit
Code: (dl )
1
2
3
4
5
if(defined $var 
and $var =~ /dies ist ein langer RegEx/
and $var =~ /noch ein laengerer RegEx mit vielen Worten/
and $var =~ /test/){
}
OTRS-Erweiterungen (http://feature-addons.de/)
Frankfurt Perlmongers (http://frankfurt.pm/)
--

Unterlagen OTRS-Workshop 2012: http://otrs.perl-services.de/workshop.html
Perl-Entwicklung: http://perl-services.de/
Relais
 2007-01-06 15:53
#35055 #35055
User since
2003-08-06
2244 Artikel
ModeratorIn
[Homepage] [default_avatar]
[quote=olruebe01,03.01.2007, 18:26]Dazu fallen mir auf Anhieb 2 Fragen ein:
1: Werden die Inhalte der Textdatei automatisch geQuated?
2: Was ist, wenn ich einen Inhalt erst verändern möchte? Wenn ich also z.B. ein Datum in (jeder Zeile) der Textdatei in Sekunden wandeln möchte?

Lässt sich das lösen?[/quote]
Zu 1.) Jein - ich weiß nicht, was Du genau meinst, aber ich vermute, daß Du Fehler bei String/Nummern-Umwandlung befürchtest: Da gibt es normalerweise gibt kein Problem, sondern eben nur, wenn Du ganz grobe Schnitzer einbaust (unpassenden Text in ein Datumsfeld schreiben wollen etwa), probier es aus.

Zu 2.) Ja. Dann nimmst Du die alte Textdatei, und schreibst mir Perl eine neue, die die nötigen Veränderung hat und ladst die.
Erst denken, dann posten --
26. Deutscher Perl- u. Raku -Workshop 15. bis 17.04.2024 in Frankfurt/M.

Winter is Coming
<< |< 1 2 >| >> 18 Einträge, 2 Seiten



View all threads created 2007-01-03 18:47.