Mysql liefert zu große Felder für PHP
Christian
- php
Hallo,
aus einer MySQL-Datenbank muss ich Protokolle auslesen, die schon recht groß werden können. Aktuell habe ich welche von 2MB Größe. Wenn ich diese mit mysql_query, mysql_fetch_row lese, bekomme ich nur einen
"Fatal error: Allowed memory size of 8388608 bytes exhausted (tried to allocate 2092477 bytes) in ...".
Wie kann ich das umgehen?
ini_set("memory_limit", "50M") wäre möglich, aber das ist auch nicht immer erlaubt, also nicht zuverlässig.
Hilft hier mysql_unbuffered_query()? Die Erklärung von php.net versteh ich nicht ganz, was da wo nicht gebuffert wird.
Gruß
Christian
Moin!
Hilft hier mysql_unbuffered_query()? Die Erklärung von php.net versteh ich nicht ganz, was da wo nicht gebuffert wird.
mysql_query sendet die Abfrage an die Datenbank, empfängt dann komplett das Ergebnis, speichert es in einen Zwischenspeicher und liefert erst von diesem Zwischenspeicher aus die einzelnen Ergsbniszeilen aus.
Wenn dein SELECT jetzt zuviele Daten für diesen Zwischenspeicher liefert, dann gibts genau zwei Möglichkeiten:
1. mysql_unbuffered_query speichert die Ergebnisse nicht in einem Puffer zwischen, sondern fragt jede Zeile erst dann ab, wenn sie tatsächlich von PHP abgefragt wird. Nachteile sind: 1. Die Datenbank kann während dieser Abfrage keinerlei anderweitige SQL-Requests mit DIESER Verbindung entgegennehmen, du müßtest also in so einem Fall eine zweite Verbindung zur DB aufbauen (und dabei irgendwie den Mechanismus austricksen, der beim Versuch der Neueröffnen einer Verbindung, die identisch zu einer bestehenden ist, einfach die bestehende verwendet). 2. Du kannst im Ergebnis nicht mehr herumspringen und wahlfrei Zeilen abrufen (mysql_data_seek, mysql_num_rows).
2. Du kannst die Größe der Abfrage auch dadurch verkleinern, dass du sie in mehrere Teile zerlegst, die insgesamt jeweils in den Puffer passen. "LIMIT" bietet sich hierbei an.
Moin!
Hallihallo!
Danke für deine ausführlichen Erklärungen, jetzt ist mir der ganze Mechanismus viel klarer geworden.
mysql_unbuffered_query speichert die Ergebnisse nicht in einem Puffer zwischen, sondern fragt jede Zeile erst dann ab, wenn sie tatsächlich von PHP abgefragt wird. Nachteile sind: 1. Die Datenbank kann während dieser Abfrage keinerlei anderweitige SQL-Requests mit DIESER Verbindung entgegennehmen, [...]. 2. Du kannst im Ergebnis nicht mehr herumspringen und wahlfrei Zeilen abrufen (mysql_data_seek, mysql_num_rows).
Du kannst die Größe der Abfrage auch dadurch verkleinern, dass du sie in mehrere Teile zerlegst, die insgesamt jeweils in den Puffer passen. "LIMIT" bietet sich hierbei an.
Die Nachteile, die sich durch mysql_unbuffered_query ergeben würden, sind für mein Problem belanglos.
Allerdings habe ich das Problem, dass ich LIMIT nicht verwenden kann, weil ich ja nur eine einzige Zeile lese. Das Protokoll, was ich oben erwähnt habe, ist in einem einzigen Feld (mediumtext) gespeichert. Die unbufferd_query bringt da wohl nichts, weil ja auch alles am Stück übertragen werden muss. Und genau das müsste ich irgendwie vermeiden. Gibt es eine Möglichkeit die einzelnen Felder blockweise von der DB zu empfangen?
Gruß
Chris
Hello,
Allerdings habe ich das Problem, dass ich LIMIT nicht verwenden kann, weil ich ja nur eine einzige Zeile lese. Das Protokoll, was ich oben erwähnt habe, ist in einem einzigen Feld (mediumtext) gespeichert. Die unbufferd_query bringt da wohl nichts, weil ja auch alles am Stück übertragen werden muss. Und genau das müsste ich irgendwie vermeiden. Gibt es eine Möglichkeit die einzelnen Felder blockweise von der DB zu empfangen?
Du kannst jedes Feld einzeln abfragen :-)
Select bericht from tabelle where bedingung=stimmt;
würde Dir nur das Feld "Bericht" liefern. Solange das DBMS selbst noch genügend Speicher hat, ist das kein Problem.
Select substring(bericht,1,500000) as bericht_teil_1 from tabelle where bedingung=stimmt;
abfragen, dann
Select substring(bericht,500001,1000000) as bericht_teil_2 from tabelle where bedingung=stimmt;
usw.
wäre als Krücke auch noch möglich.
Das kannst Du dann auch innerhalb einer Schleife in Deinem Script aufrufen. Du jusst nur daran deneken, dass Resultset VOR dem neuen Select auch wieder freizugeben. Sonst erzeugst Du Lost Handles.
Harzliche Grüße aus http://www.annerschbarrich.de
Tom
Hi,
Du kannst jedes Feld einzeln abfragen :-)
Select bericht from tabelle where bedingung=stimmt;
würde Dir nur das Feld "Bericht" liefern. Solange das DBMS selbst noch genügend Speicher hat, ist das kein Problem.
Weiß ich :-)
Select substring(bericht,1,500000) as bericht_teil_1 from tabelle where bedingung=stimmt;
abfragen, dann
Select substring(bericht,500001,1000000) as bericht_teil_2 from tabelle where bedingung=stimmt;
usw.
Genau so etwas habe ich gesucht! Dank dir :)
Gruß
Chris