News - Features - Downloads - Forum - Team - Support - Switch View: Screen
Login - Registrierung - Passwort vergessen

Antworten: 12
Seite [1]
Slevin


Rock the board





Beiträge: 76
# Thema - 19.04.2009 um 23:20 Uhr
Hi

- Für ein zukünftiges Modul

Ich bin gerade dran ein File in die Sql Datenbank zu übernehmen.
Dieses File hat ungefähr 4000 (+/- 2000) Zeilen, mit 65 Daten pro Zeile getrennt durch ein Komma.
Ich habe es auch hinbekommen eine Funktion dafür zu schreiben, nur ist die so wie es aussieht zu systemauslastend:
Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 7390759 bytes) , ups ^^.
Ich habe nun die maximale Zeileneingabe in die Datenbank auf 2000 begrenzt, und die Auslastung des Systems passt dann schon, aber so wie ihr euch vorstellen könnt dauert es halt dann schon ziemlich lange, und die Page steht dann in dem "Wartevorgang ", bis die Funktion fertig ist.

Habt ihr vlt ne bessere Lösung parat? Wäre cool!

 
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
1. / 2. / ... 
 $SqlColumnNamesArray = array( '....hier sind ganz dolle 65 Spalten ... ');
$TableArray = array();
$FileArray file$file );
$MaxRow 0;
$TableArray[0] = '';
foreach (
$FileArray as $FileRowString)
{
    
$MaxRow++;
    
$FileRowArray explode(","$FileRowString65);
    for (
$i 0$i 65$i++)
    {
        
$TableArray[$i 1] = $FileRowArray[$i];
    }
    
cs_sql_insert(__FILE__'xXxXx'$SqlColumnNamesArray$TableArray);
    if (
$MaxRow == 2000) break;
}


Grüße Haba


------------------
Inaktiv
SCHIRI ClanSphere Team


Weltmeister



Herkunft: Hamburg
Beiträge: 5299
# Antwort: 1 - 19.04.2009 um 23:25 Uhr
Ein gute Lösung gibt es dafür nicht.
Ein workaround wäre, das Formular mit dem die Datei hochgeladen wird in einen Frame zu submiten, damit der Benutzer vom Ladevorgang nichts mitbekommt.

Allerdings ist dein Script auch nicht so performant, wenn ich das richtig sehe.
ich würde ERST den SQL-Query string bauen und NACHDEM die schleife fertig ist, nur einen Query an die DB schicken. So wie du es jetzt hast, hast du ja 2000 SQL-Queries bei einem Seitenaufruf.


------------------
www.laszlokorte.de

Inaktiv
|
Slevin
Thread-Ersteller


Rock the board





Beiträge: 76
# Antwort: 2 - 19.04.2009 um 23:36 Uhr
Schade
zu 1) hmmm muss ich mal informieren wie ich das am besten anstelle, habe ich noch nie gemacht.
zu 2) ja hast du richtig gesehen, ist die frage ob es so schneller geht bei einem einzigen Sql-Query, teste ich mal

Danke Schiri


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


Inaktiv
|
SCHIRI ClanSphere Team


Weltmeister



Herkunft: Hamburg
Beiträge: 5299
# Antwort: 3 - 19.04.2009 um 23:40 Uhr
naja wie das mit dem Frame geht:

einfach einen (i)Frame erstellen, einen Namen geben und beim formular target="NAME_DES_FORMS" angeben.

2.:
Du kannst ja auch den SQL-Query immer nach 100 Zeilen oder so schicken und dann einen neuen Query zusammen bauen. Aber 2000 Querys sind wohl das langsamste, was geht.


------------------
www.laszlokorte.de

Inaktiv
|
Slevin
Thread-Ersteller


Rock the board





Beiträge: 76
# Antwort: 4 - 20.04.2009 um 00:30 Uhr
das mit dem iframe klappt schon mal ziemlich gut. (gar nichts so schwer )
Das Eintragen in die DB werde ich auch noch etwas überarbeiten, hast schon recht.
Werde mal beide Varianten von dir testen.
Danke nochmals!

Wenn keiner mehr noch was weiß, dann kann man schließen!


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


Inaktiv
|
Fr33z3m4n ClanSphere Team


Medal of Honor




Herkunft: Hamm
Beiträge: 11094
# Antwort: 5 - 20.04.2009 um 08:56 Uhr
Wenn es doch so viele Zeilen etc sind, würde ich es als Block machen.
Immer max 10 Query abschicken, oder je nachdem, bloß nicht so viele, wenn das fertig ist, würde ich eine Ausgabe produzieren, und schreiben "Erster Block fertig" oder so ähnlich.
Aber erst weitermachen, wenn der block wirklich 100% true gibt, ansonsten canceln und ggf. fehlermeldung bringen.
So reduzierst du die Wartezeit, da du ja jedesmal eine Statusmeldung bekommst.
Kannst ggf. noch eine warteschleife einbauen, damit der nächste query etwas verzögert eingespielt wird.


------------------
mfg
Patrick "Fr33z3m4n" Jaskulski

Antoine de Saint-Exupéry: Wenn Du ein Schiff bauen willst, so trommle nicht Männer zusammen, um Holz zu beschaffen, Aufgaben zu verteilen, sondern lehre die Männer die Sehnsucht nach dem endlosen weiten Meer.

Inaktiv
|
SCHIRI ClanSphere Team


Weltmeister



Herkunft: Hamburg
Beiträge: 5299
# Antwort: 6 - 20.04.2009 um 09:17 Uhr
hatte ich ja auch vorgeschlagen, aber die zwischenzeitige ausgabe funktioniert ja nicht, weil das ja nur ein php-script ist, was die antwort erst nach vollendung an den browser schickt.

du könntest aber auch die datei erstmal nur aufm server speichern, zusammen mit einem wert, wie weit die datei schon in die Datenbank gespielt wurde und dann nur 100 Zeilen einspielen, dann antwortschicken, den benutzer auf weiter klicken lassen (oder per js oder meta automatisch refreshen) und damit das script von vorne starten, aber dann bei der 100. Zeile beginnen lassen und so weiter.


------------------
www.laszlokorte.de

Inaktiv
|
duRiel ClanSphere Team


Weltmeister




Herkunft: Cambridge
Beiträge: 7300
# Antwort: 7 - 20.04.2009 um 09:43 Uhr
20.04.2009 um 08:17 Uhr - SCHIRI:
hatte ich ja auch vorgeschlagen, aber die zwischenzeitige ausgabe funktioniert ja nicht, weil das ja nur ein php-script ist, was die antwort erst nach vollendung an den browser schickt.


flush()


Inaktiv
|
SCHIRI ClanSphere Team


Weltmeister



Herkunft: Hamburg
Beiträge: 5299
# Antwort: 8 - 20.04.2009 um 09:46 Uhr
Man lernt nie aus^^


------------------
www.laszlokorte.de

Inaktiv
|
Slevin
Thread-Ersteller


Rock the board





Beiträge: 76
# Antwort: 9 - 20.04.2009 um 13:09 Uhr
Die Datei wo ich in die Sql-Tabelle schreibe, wird vorher von einem FTP Server gedownloaded, von dem her ist die dann unter uploads drin.
flush() werde ich mir mal anschauen... danke duRiel
Das insert werde ich defenitiv mal in blöcke machen, mal etwas rumtesten.
Für den aufruf der insert funktion werde ich nun in einem iframe bauen, und dort vlt noch ein paar Spielereien einbauen, mit grafischer Anzeige usw.
Das kann aber noch etwas dauern, da diese woche viel Arbeit bei mir angesagt ist, ich halte euch mal auf dem laufenden wie sich es entwickelt hat.
Und dann kurze Zeit später werde ich es endlich veröffentlichen können, mein Modul….jippi
Außer es kommt wieder eine neue Clansphere Version, wo ich wieder so viel umbauen muss


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


Inaktiv
|
Slevin
Thread-Ersteller


Rock the board





Beiträge: 76
# Antwort: 10 - 08.05.2009 um 13:24 Uhr
Hi
So ich habe mal ein paar Versionen ausprobiert, und wie versprochen schicke ich euch mal das Ergebniss.
z.B. mit file() oder fopen(), diese beiden Varianten auch mit einzelnen Statements (ein Statement pro Zeile), oder auch ganzen Statements (also komplettes File in einem Statement).

Das was aber ziemlich schnell klar wurde ist, das in der cs_sql_query Funktion die log Funktion ausgeschaltet werden müsste für meine Zwecke.
Beispiel: Ich schreibe 2000 Zeilen mit 65 Feldern in die Datenbank mit dem Code den ihr unten seht (ganz unten).
Ohne Log Funktion = 2MB
Mit Log Funktion = 28MB
Das ist schon ein unterschied.
Jetzt kommt meine Frage.
Könnte ihr bei den cs_sql_query Funktionen z.B. noch so was hinzufügen (siehe MYSQL-QUERY von Haba), bei dem nächsten Release, wer weiß vlt wird das irgendwann mal von jemand anderem auch mal benötigt? (ich will sowieso mein Modul erst veröffentlichen wenn die Stable Version On ist)
Oder, habe ich vlt was übersehen wo ich diese Funktion deaktivieren kann, durch irgendeinen Aufruf?
Oder, soll ich als Modul Coder die PHP-Datein verändern für die Datenbanken und für den User mitliefern? (wäre ja auch irgendwie blöd)
 
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
1. / 2. / ... 
 // Log abschaltbar
function cs_sql_query($cs_file$sql_query$log true)
{
  global 
$cs_db;
  
$sql_query str_replace('{pre}'$cs_db['prefix'], $sql_query);
  if (
mysql_query($sql_query$cs_db['con'])) {
    
$result = array('affected_rows' => mysql_affected_rows($cs_db['con']));
  } else {
    
cs_error_sql($cs_file'cs_sql_query'mysql_error($cs_db['con']));
    
$result 0;
  }
  if (
$log == truecs_log_sql($cs_file$sql_query);
  return 
$result;
}


So nun weiter im Text.
Das File das ich benutze, um dieses in die Datenbank zu schreiben, hat 65 Felder und 5000 Zeilen.
Wer die Funktion file() kennt, weiß ja das das Komplette File in ein Array geschrieben wird, also kurz gesagt für meine Anforderungen recht ungeeignet, da die Datei vlt auch etwas größer werden könnte.
Da ist die Variante mit fopen() und fgets schon Ressourcen schonender.
Nun hat sich die frage gestellt, ein komplettes Sql Statement für das ganze File, oder für jede Zeile ein Statement, oder in Blöcken.
Wie ich schnell gemerkt habe, ist ein komplettes Statement nicht besonders doll, da ich bei meinem Xammp System schnell an die grenzen gestoßen bin.
Da ein SQL Statement "nur" 1Mb haben darf, und ich dieses dann schnell überschritten habe.
Die letzte und auch beste Variante für meine Zwecke ist, die SQL Statements in Blöcken zu schicken.
Ich schicke nun alle 1500 Zeilen einen SQL Block, und das funzt ganz gut.

Messwerte mit fopen() und fgets (Log Funktion abgeschaltet):
für jede Zeile ein SQL Staitment:
- 4-5 Sekunden Ladezeit und einen Verbrauch von 1.2 MiB [peak 1.6 MiB]
SQL Statements in 1500er Blöcken:
- 2 Sekunden Ladezeit und einen Verbrauch von 1.2 MiB [peak 5.19 MiB]; Mit dieser Variante sind 68000 Zeilen möglich, bis die PHP Begrenzung zuschlägt (1.2 MiB [peak 31.25 MiB])


 
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.
1. / 2. / ... 
 $SqlColumnName '... 65 Felder ...';

// Überprüfen ob es sich um eine Windows-Textdatei oder eine Linux-Textdatei handelt. \r\n == Windows; \n == Linux
$FileHandle fopen($File"r");
$FileRow fgets($FileHandle);
$i strlen($FileRow) - 2;
if (
$FileRow[$i] == "\r"$BreakChar = -4; else $BreakChar = -2;
fclose($FileHandle);


$SqlInsert "";
$RowCounter 0;
$RowArray = array();
$Datasets = array();

$FileHandle fopen($File"r");
while(
$FileRow fgets($FileHandle))
{
    
$Datasets explode(","$FileRow65);
    if (
Count($Datasets) != 65)
    {
        
fclose($FileHandle);
        return;
    }
    
// Wir checken das letzte Datenfeld (ist für einen Namen bestimmt) auf Gültigkeit.
    
$s cs_sql_escape($Datasets[64]);
    if (
$s == false)
    {
        
$s "Hacker ;)";
    }
    
$Datasets[64] = substr($s0$BreakChar);
    
    
// Wir schreiben alle Elemente wieder zusammen in einen String. Wir benutzen nicht Replace() dafür weil wir auch ein Komma im Datenfeld für den Namen haben können.
    
$s "";
    for (
$i 0$i 65$i++)
    {
        
$s $s $Datasets[$i] . "','";
    }
    
$s substr($s0, -2);
    
$s "('" $s ")";

    
// Wir schreiben die fertige Datensatz-Zeile in unser Array und zählen den ZeilenZähler hoch.
    
$RowArray[$RowCounter] = $s;
    
$RowCounter++;
}
fclose ($FileHandle);

$SqlInsert "REPLACE INTO {pre}_cssstats_rank (" $SqlColumnName ") VALUES ";
for (
$i 0$i Count($RowArray); $i++)
{
    if ((
$i != 0) && ($i 1500 == 0))
    {    
        
// false => für die abgeschalteten LOGs
        
cs_sql_query(__FILE__substr($SqlInsert0, -1), false);
        
$SqlInsert "REPLACE INTO {pre}_cssstats_rank (" $SqlColumnName ") VALUES ";
    }

    
$SqlInsert .= $RowArray[$i] . ",";
}
cs_sql_query(__FILE__substr($SqlInsert0, -1), false);


Fazit:
Ein Query ist mal verdammt schnell. ^^
Bei größeren Datenmengen fopen() verwenden, und nicht file().
Die Log Funktion ist "etwas" Ressourcent fressend

mfg
Haba - Nils


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


Inaktiv
|
hajo ClanSphere Team


VIP - Poster




Herkunft: Barsbüttel
Beiträge: 9411
# Antwort: 11 - 08.05.2009 um 16:33 Uhr
mh die müsste eigtl nur im debug modus mitlaufen wohl


------------------
ClanSphere - professional clan care starts here

Inaktiv
|
Slevin
Thread-Ersteller


Rock the board





Beiträge: 76
# Antwort: 12 - 08.05.2009 um 22:01 Uhr
Oh ja da hast du recht, das ist mir ja jetzt schon etwas peinlich ^^.
Habe mich schon gefragt wo er das Array benutzt, wenn bei der Log Funktion der Parameter "action = 0" ist. Bin nicht auf die Überlegung gekommen das er es ja im Debug Modus Oben rauskotzt, und das er dann durch die Anzeige so viel verbraucht *boing*

Aber auch ohne den Debug Modus füllt er das Array, und es gehen dann 6MB (bei 5000 Zeilen) flöten die ich nicht benutze. Auch irgendwie eine Verschendung von Ressourcen.
Wäre es nicht sinnvoller das Array erst zu befüllen wenn der Debug Modus an ist?


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


Inaktiv
|
Antworten: 12
Seite [1]


Sie müssen sich registrieren, um zu antworten.


ClanSphere Project - Mailus - Imprint - Disclaimer - Scriptinfo