Seite 1 von 1

Viele Dateien schnell einlesen

Verfasst: Donnerstag 26. November 2009, 20:03
von Barabbas
Hallo zusammen,

ich möchte 2.500.000 Dateien einlesen und in eine Datenbank packen. Hört sich doof an, ist aber so. Konkret geht es um die FreeDB.

Mein Ansatz bisher:
Mit os.listdir die Verzeichnisse auslesen, alle Dateien in eine Liste packen und über diese dann iterieren.
Dabei wird jede einzelne Datei geöffnet, in Zeilen aufgesplittet und die Zeilen mit den relevanten Infos (Artist...) werden in ein dict gepackt.

Das dict wiederum wird in eine Liste gepackt. Sind mehr als 1000 Einträge in der Liste, werde diese mit executemany() in meine SqliteDB gepackt und die Liste geleert.

Ist sau langsam? Was ist mein größter Fehler (abgesehen davon, dass ich Sqlite benutze?)

Danke schonmal - ist zwar nur eine Spielerei, aber mich interessiert schon, wo meine Fehler liegen.

Verfasst: Donnerstag 26. November 2009, 20:13
von ms4py
Du solltest natürlich nicht alle Dateienamen in eine Liste packen, dann musst du ja zuerst eine Liste mit 2.5 Mio Einträgen erstellen.
Verwende stattdessen os.walk

Allerdings wird das trotzdem noch extrem langsam sein, geht ja auch gar nicht anders bei der Menge an Daten!

Verfasst: Donnerstag 26. November 2009, 20:27
von Defnull
Unter MySQL hätte ich "INSERT DELAYED" empfohlen. Generell würde ich noch jede Form von Index löschen und nach dem einlesen wieder neu erstellen, da ein INSERT sonst immer jeden betroffenen Index updaten muss.

Verfasst: Donnerstag 26. November 2009, 20:48
von Barabbas
Danke schonmal,

das mit der Liste am Anfang ist wirklich etwas mächtig. Es dauert ca. 10-20 Sekunden, bis der "Index" aller Dateien erstellt ist. Allerdings hat das ja später keinen Einfluss mehr auf die Performance.
Ich schaffe zur Zeit ca. 100 Dateien / Sekunde - inklusive Insert. Einen Index hat die Datenbank nicht. Vll. ist tatsächlich jetzt schon die Platte der Flaschenhals. Irgendwie ernüchternd *g.

Schöne Grüße,

brb

Verfasst: Donnerstag 26. November 2009, 20:57
von ms4py
Barabbas hat geschrieben: Ich schaffe zur Zeit ca. 100 Dateien / Sekunde - inklusive Insert. Einen Index hat die Datenbank nicht. Vll. ist tatsächlich jetzt schon die Platte der Flaschenhals.
Natürlich ist da der HD-Zugriff das Bottleneck, die Datenbank an sich schreibt ja auch wieder auf die Platte.

Und weißt meinst du mit "jetzt schon"? Ist das nicht ein einmaliger Vorgang, den du halt einmal durchlaufen lässt und dann ist gut?

Verfasst: Freitag 27. November 2009, 08:24
von jens
btw. Wenn man vor dem eigentlich abarbeiten eine Liste der Dateien, bzw. die Anzahl ermittelt, kann man damit beim eigentlichen Import eine Fortschrittsanzeige bauen.

Ansonsten einfach mal ein wenig messen wobei die meiste Zeit drauf geht.

Verfasst: Freitag 27. November 2009, 09:04
von CM
Eine Liste mit, oder besser ein Iterator über, 2.5e6 Einträge ist gar nicht so absurd viel.

Wenn Du über eine Multicoremaschine verfügst, kannst Du ggf.

Code: Alles auswählen

import multiprocessing
pool = multiprocessing(Günstige Zahl der Prozesse)
pool.map(inserter, fnames)
pool.close()
Machen, wobei inserter eine Funktion ist, die einen Filenamen akzeptiert und in Deine DB schreibt und fnames ein Iterable mit den ganzen Pfaden.

Vielleicht hast Du ein schwerwiegendes I/O-Bottleneck, aber so etwas wie oben mache ich z. Zt. ständig (mit weniger, aber größeren Dateien) und stelle durchaus eine wesentliche Beschleunigung fest. Und das ist nur die syntaktisch einfachste Variante - nicht wirklich sophisticated. Ggf. ist Threading auch eine bessere Alternative. Einfach mal probieren.

HTH
Christian

Verfasst: Freitag 27. November 2009, 10:51
von Defnull
SQLite ist nicht gerade für Performance bekannt und das Bottleneck ist hier sehr wahrscheinlich IO. Da aber der gesamte IO auf die selbe Ressource (eine einzelne Festplatte) abzielt, macht Nebenläufigkeit keinen Sinn und könnte die Situation sogar noch schlimmer machen. Ich würde multiprocessing oder threads erst einmal bei Seite lassen und vorher heraus finden, wo genau die Zeit verbraucht wird. Wenn es wirklich am IO liegt, hilft nur ne schnellere Festplatte.

http://docs.python.org/library/profile.html

@CM: Wenn IO das Problem ist, macht das multiprocessing Modul keinen Sinn. Dafür sind Threads erfunden worden. Multiprocessing bringt nur etwas bei CPU intensiven Operationen auf multicore Architekturen. Leider wird das multiprocessing Modul in dieser Hinsicht sehr oft falsch verwendet.

Verfasst: Freitag 27. November 2009, 11:01
von CM
Ok, gute Argumente.
Meine Bemerkung, dass u. U. Threading vorzuziehen ist, hätte ich stärker formulieren sollen und erst ein Profiling zu machen und dann zu entscheiden ist ohnehin sinnvoller - sorry, war nicht sonderlich überdacht von mir. Danke für die Klarstellung.

Verfasst: Freitag 27. November 2009, 19:30
von BlackJack
SQlite ist nicht auf mehere Schreiber ausgelegt. Wenn das mehrere gleichzeitig machen, dann wird immer für jeden die gesamte DB gesperrt und die anderen müssen warten. Da ist also auch nicht viel mit Nebenläufigkeit zu holen.

@Barabbas: Wird denn auch alle 1000 Einträge ein `commit` gemacht? Oder wird die "Transaktionsmasse" immer grösser? Und müssen es Dictionaries sein? Listen sind vielleicht schneller.

Verfasst: Freitag 27. November 2009, 21:31
von problembär
http://www.sqlite.org/faq.html#q19

ist bekannt, oder?

Gruß

Verfasst: Samstag 28. November 2009, 00:12
von gerold
Defnull hat geschrieben:SQLite ist nicht gerade für Performance bekannt
Hallo Defnull!

SQLite ist als Desktopdatenbank (nur ein Benutzer, keine konkurierenden Schreibzugriffe) sauschnell. Da SQLite als "einfache" Embedded-Datenbank konzipiert wurde, gibt es kaum bremsenden Overhead.

mfg
Gerold
:-)

Verfasst: Samstag 28. November 2009, 01:22
von nemomuk
SQLite ist doch eigentlich super schnell, nur eben nicht wirklich auf multi ausgelegt.

EDIT: zu spät^

Verfasst: Samstag 28. November 2009, 20:17
von Barabbas
Hallo nochmal,

danke für eure Hinweise und Tipps, ich werde morgen erstmal das Profiling in Angriff nehmen.

@ice2k3: Ja klar, ist eigentlich ein einmaliger Vorgang. Aber man will ja - wenn man mal experimentiert - das Ganze möglichst schnell durchlaufen lassen.

"mutliprocessing" für Listen kannte ich auch noch nicht, mal sehen, was das Profiling ergibt.

Tatsächlich ist die FreeDB entpackt auch > 9 GB in kleinen Files. Hatte ich vorher gar nicht so vor Augen. Von daher scheint die Geschwindigkeit echt ok zu sein.

Was INSERT, Festplattenrotation etc. angeht: Zur Zeit werden ja jeweils 1000 Einträge committed. Die hat das Skript nach ca. 10 Sekunden zusammen. Ich denke, bei inserts in 10 Sekunden Abständen gibt in dieser Hinsicht es recht wenig zu optimieren - auch, weil die quasi instantan gehen.

Schöne Grüße,

brb