YellowLed am :
"Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 77 bytes) in /www/htdocs/xxx/include/db/mysql.inc.php on line 135"
Hm?
"Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 77 bytes) in /www/htdocs/xxx/include/db/mysql.inc.php on line 135"
Hm?
Ach ja, vergessen: Das bekomme ich beim Versuch, meine DB zu exportieren. Import habe ich noch nicht versucht.
Du musst an der Import-Funktion noch ein bisschen feilen. Ich bekomme einen Timeout, wenn ich eine 200 kB Datei einlesen möchte.
Ich habe den Code mal hier abgelegt.
Für jede Zeile in der Datei führt er ein Select aus, um zu prüfen ob der Eintrag schon vorhanden ist, um dann den Eintrag entweder anzulegen oder den bestehenden zu erhöhen.
Ich sehe gerade nicht, wie man das grundsätzliche Vorgehen besser machen kann. Kann man aber vielleicht am SQL etwas verbessern? Ich wollte zuerst mit einem if in SQL (den unoptimierten Code) arbeiten, aber das funktionierte in meinem Test nicht.
Also, die erste Idee wäre ein Schlüssel auf token, sollte das nicht reichen, können wir noch einen Primärschlüssel auf token und type legen. Ich probiere das mal.
Ich habe befürchtet, dass es bei großen Datenbanken Ärger macht. Beim Timeout beim Importieren werde ich wenig machen können, außer zu versuchen, die Timeoutgrenze abzuschalten. Wobei: ich hänge gleich an deinen Kommentar mal das bisherige Vorgehen samt sql an, vielleicht kann man das ja verbessern.
Die Memorygrenze beim Export ist hart. Kannst du die noch weiter erhöhen, mal testweise auf 128 MB setzen? So könnten wir versuchen auszuschließen, dass du da in eine Endlosschleife läufst.
Wenn man die Größe wüsste, ab der er beim Import den Timeout wirft, könnte man natürlich die CSV-Dateien in handliche Portionen aufsplitten.
Was die Erhöhung der Memory-Grenze angeht: Gerne, aber ich habe keine Ahnung, wo und wie. :-) php.ini? .htaccess?
php_value memory_limit 128M
in der .htaccess könnte ausreichen.
Ja, mit 128M funktioniert es dann.
Knapp 27.000 Zeilen sind auch nicht so wahnsinnig gross für eine Datenbank-Tabelle. Ich vermute, dass der fehlende Index das grösste Problem ist.
Der index hilft nicht bei der Memorygrenze, oder? Beim Insert auch eher nicht, oder? Aber beim Update und Select?
Du fragst pro importierter Zeile nach token und type. Genau dafür sind Indizes da. Ein Problem ist allerdings, dass auch doppelte enthalten sind. Meiner Meinung nach sollte das Tupel token+type eindeutig sein.
Ja, token und type ist eindeutig. Früher war token der primary key, das wurde dann bei der Einführung von type entfernt.
Nachtrag: Der Index hilft nicht bei der Memory-Grenze.
Bei mir ist Token+Type nicht eindeutig, es gibt doppelte.
Ich schlage erst einmal folgendes vor:
Das Type-Feld kann maximal einen von sechs Werten enthalten. Da wäre das bessere Datenformat "Set" oder eventuell auch "Byte".
Jeder Datensatz sollte eine eindeutige ID bekommen (auto_increment-Feld). Dann kann man mit "select a.id,a.token,a.type from tabelle a, tabelle b where a.token=b.token and a.type=b.type and a.id<>b.id" die Datensätze herausfinden die Doppelt sind.
Anschliessend summiert man die gleichen Datensätze zusammen: "select sum(ham),sum(spam) where id in (1,2,3,4)" oder gleich als neuer Datensatz "insert into tabelle (token,type,spam,ham) values (wert1,wert2,select sum(ham),sum(spam) where id in (1,2,3,4))" und anschliessend "delete from tabelle where id in (1,2,3,4)".
Das Verfahren kann man auch für den Importieren nutzen, einfach die neuen Datensätze hinzuladen und dann die doppelten mit dem obigen Verfahren rauswerfen. Vielleicht immer nur 100 auf einmal, um Timeouts vorzubeugen.
Alternativ dazu kann man auch mit zwei Temporärtabellen arbeiten. Das wird aber aufwendiger.
Hi Dirk. Ich finde sowohl manuell als auch mit deiner Methode keine doppelten Datensätze in deiner CSV-Datei, die ich inzwischen in meinen Testblog importiert habe (dort ist der Timeout scheinbar nicht gesetzt und es klappt).
mysql> ALTER TABLE serendipity_spamblock_bayes ADD id INT UNSIGNED NOT NULL AUTO_INCREMENT, ADD PRIMARY KEY (id);
mysql> select a.id, a.token, a.type from serendipity_spamblock_bayes as a, serendipity_spamblock_bayes as b where a.token=b.token and a.type=b.type and a.id <> b.id;
Empty set (4 min 47.68 sec)
Das lässt drei Möglichkeiten offen:
1. Es gibt keine doppelten Datensätze (das legt der Code auch nahe)
2. Die doppelten Datensätze kamen durch den Import hinein.
3. Der SQL-Befehl funktioniert nicht.
Ich prüfe das jetzt, indem ich deine Datei nochmal importieren (und so hoffentlich Option 2 ausschließe).
mysql> ALTER TABLE serendipity_spamblock_bayes ADD UNIQUE ( token , type );
ERROR 1062 (23000): Duplicate entry 'hatte-body' for key 1
Das sind die Ursprungsdaten ohne Import. Gross- und Kleinschreibung scheint nicht unterschieden zu werden:
mysql> select * from serendipity_spamblock_bayes where token='hatte' and type='body';
+-------+-----+------+------+
| token | ham | spam | type |
+-------+-----+------+------+
| Hatte | 64 | 2 | body |
| hatte | 64 | 2 | body |
| h?tte | 35 | 5 | body |
| Hatte | 64 | 2 | body |
+-------+-----+------+------+
4 rows in set (0.00 sec)
Ha. Ok. Dem ist bei mir nicht so. Der Befehl läuft ohne Duplikate durch. Was wohl daran liegt, dass er beim Import nur etwas einfügt wenn das Select vorher nichts fand, wodurch die Duplikate vermeiden wurden.
Der Index beschleunigt den SELECT von oben extrem, die Idee ist gut. Ich werde (wohl nach dem Wochenende) die Datenbanksituation mit Duplikaten nachstellen, wie von dir beschrieben die Doppelten zusammenfassen und danach den Index drüberlegen.
Hast Du den doppelten Index genommen? Diesen hier "ALTER TABLE serendipity_spamblock_bayes ADD UNIQUE ( token , type );"?
Denkst Du bitte daran, aus Type auch noch ein enum-Feld zu machen?
(Hast Du meine Mail bezüglich Auto-Titel bekommen?)
Ja, hatte genau den Index genommen. Vorher dauerte die Suche nach Duplikaten 5 Minuten, nachher nichtmal eine.
>Denkst Du bitte daran, aus Type auch noch ein enum-Feld zu machen?
Da bin ich mir unsicher. SQLite z.B. kennt sowas gar nicht - ich fürchte, damit in Probleme zu laufen.
(Ja, aber zu einem ungünstigen Zeitpunkt, wodurch ich sie vergaß. Sorry. Ich schau mir das an.)
Statt enum kannst Du auch ein Feld vom Typ Byte (Integer für SQLite) nehmen, dann musst Du aber die Nummern hart codieren. Beides ist um Klassen besser als VarChar.
Dieses hartkodieren würde ich gerne vermeiden. Später die Tabelle nicht mehr lesen zu können könnte sehr störend bei der weiteren Entwicklung werden. Da meiner Einschätzung nach der Import nun auch so gut möglich ist, lasse ich das erstmal sein.
Ich behalte das aber im Kopf falls mehr Verbesserungen nötig werden sollten.
Ich habe das Update soeben hochgeladen, werde später mehr dazu schreiben. Es sollte sowohl Import als auch Export mit größeren Datebanktabellen ermöglichen. Danke schonmal, euch beiden für das Melden der Bugs und der Hilfe bei der Lösung.
Dirks Logbuch am : Spamschutz Bayes ...
Vorschau anzeigen
matthias.yellowled.de am : PingBack
Vorschau anzeigen
onli blogging am : Bayes 0.3.9.1: Datenbankperformance
Vorschau anzeigen