Viele Dateien schnell einlesen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

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.
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

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!
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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.
Bottle: Micro Web Framework + Development Blog
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

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
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

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?
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

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
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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.
Bottle: Micro Web Framework + Development Blog
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

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.
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.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

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
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

SQLite ist doch eigentlich super schnell, nur eben nicht wirklich auf multi ausgelegt.

EDIT: zu spät^
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

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
Antworten