2010-05-21 6 views
10

Bitte ertragen Sie mit mir, wie ich das Problem erkläre, wie ich versuchte, es zu lösen, und meine Frage, wie es zu verbessern ist, ist am Ende.Wie verwendet man SQLAlchemy, um eine SQL-Datei aus Abfrageausdrücken zum Masseneinfügen in ein DBMS auszugeben?

Ich habe eine 100.000 Zeilen CSV-Datei von einem Offline-Batch-Job, und ich musste fügen Sie es in die Datenbank als seine richtigen Modelle. Normalerweise, wenn dies eine ziemlich einfache Last ist, kann dies einfach geladen werden, indem man die CSV-Datei einfach an ein Schema anpasst; aber ich musste einige externe Verarbeitungen durchführen, die Abfragen erfordern, und es ist einfach viel bequemer, SQLAlchemy zu verwenden, um die Daten zu generieren, die ich möchte.

Die Daten, die ich hier haben möchte, sind 3 Modelle, die 3 vorhergehende Tabellen in der Datenbank darstellen und jedes nachfolgende Modell hängt vom vorherigen Modell ab. Zum Beispiel:

Model C --> Foreign Key --> Model B --> Foreign Key --> Model A 

So müssen die Modelle in der Reihenfolge A, B und C. Ich kam mit einem Erzeuger/Verbraucher-Ansatz eingeführt werden bis:

- instantiate a multiprocessing.Process which contains a 
threadpool of 50 persister threads that have a threadlocal 
connection to a database 

- read a line from the file using the csv DictReader 

- enqueue the dictionary to the process, where each thread creates 
the appropriate models by querying the right values and each 
thread persists the models in the appropriate order 

Das war schneller als ein non-threaded lesen/persist, aber es ist viel langsamer als Bulk-Laden einer Datei in die Datenbank. Die Arbeit endete persistent nach etwa 45 Minuten. Aus Spaß, entschied ich mich, es in SQL Aussagen schreiben, dauerte es 5 Minuten.

Das Schreiben der SQL-Anweisungen dauerte jedoch ein paar Stunden. Also meine Frage ist, könnte ich eine schnellere Methode verwendet, um Zeilen mit SQLAlchemy einfügen? Wie ich es verstehe, SQLAlchemy ist nicht für Massen-Operationen ausgelegt, so dass dies weniger als ideal ist.

Dies folgt auf meine Frage, gibt es eine Möglichkeit, die SQL-Anweisungen mit SQLAlchemy zu generieren, in einer Datei zu werfen, und dann einfach eine Massenladung in die Datenbank verwenden? I wissen über str (model_object), aber es zeigt nicht die interpolierten Werte.

Ich würde jede Anleitung schätzen, wie man das schneller macht.

Danke!

+1

Haben Sie den Autocommit-Modus deaktiviert? Es wird in der Regel schneller, wenn Sie mehrere Zeilen einfügen und dann Commit statt nach jedem Einfügen committen. –

Antwort

2

Erstens, es sei denn, Sie haben tatsächlich eine Maschine mit 50 CPU-Kernen, die Verwendung von 50 Threads/Prozesse wird die Leistung nicht helfen - es wird tatsächlich die Dinge langsamer machen.

Zweitens habe ich das Gefühl, dass, wenn Sie SQLAlchemy Weg inserting multiple values at once verwenden, wäre es viel schneller als ORM-Objekte erstellen und sie einzeln beibehalten.

+0

Tut mir leid, dass ich diese Antwort nicht früher angenommen habe, aber dies ist der richtige Ansatz. –

2

Normalerweise, nein, es gibt keine Möglichkeit, die Abfrage mit den Werten zu erhalten.

Welche Datenbank verwenden Sie? Ursache viele Datenbanken haben einige Bulk-Load-Funktion für CSV verfügbar.

Wenn Sie bereit sind, dass bestimmte Werte annehmen möglicherweise nicht korrekt entwertet werden, als Sie diesen Hack verwenden können Ich schrieb für Debugging-Zwecke:

'''Replace the parameter placeholders with values''' 
params = compiler.params.items() 
params.sort(key=lambda (k, v): len(str(k)), reverse=True) 
for k, v in params: 
    '''Some types don't need escaping''' 
    if isinstance(v, (int, long, float, bool)): 
     v = unicode(v) 
    else: 
     v = "'%s'" % v 

    '''Replace the placeholders with values 
    Works both with :1 and %(foo)s type placeholders''' 
    query = query.replace(':%s' % k, v) 
    query = query.replace('%%(%s)s' % k, v) 
0

Ich wage zu sagen, dass die Zeit, die im Python-Skript verbracht wird, im Upload-Teil pro Datensatz ist. Um dies zu ermitteln, könnten Sie in CSV schreiben oder die Ergebnisse verwerfen, anstatt neue Datensätze hochzuladen. Dies wird bestimmen, wo der Engpass ist; zumindest von einem Lookup-vs-Insert-Standpunkt. Wenn, wie ich vermute, das ist, wo es ist, können Sie die Bulk-Import-Funktion nutzen, die die meisten DBS haben. Es gibt keinen Grund, und in der Tat einige Argumente dagegen, in diesem Fall Rekord für Satz einzufügen.

Bulk-Importe neigen dazu, einige interestng Optimierung wie tun es als eine Transaktion w/o commits für jeden Datensatz (auch nur dies tun könnte einen deutlichen Rückgang der Laufzeit sehen) tun; Wann immer es machbar ist, empfehle ich den Bulk-Insert für große Datensätze. Sie können weiterhin den Producer/Consumer-Ansatz verwenden, aber der Konsument speichert die Werte stattdessen im Speicher oder in einer Datei und ruft dann die Bulk-Import-Anweisung auf, die für die von Ihnen verwendete DB spezifisch ist. Dies könnte der Weg sein, den Sie gehen müssen, wenn Sie die Verarbeitung für jeden Datensatz in der CSV-Datei durchführen müssen. Wenn ja, würde ich auch überlegen, wie viel davon zwischen den Datensätzen zwischengespeichert und geteilt werden kann.

ist es auch möglich, dass der Engpass SQLAlchemy verwendet. Nicht, dass ich irgendwelche inhärenten Probleme kenne, aber angesichts dessen, was Sie tun, könnte es viel mehr Verarbeitung als nötig erfordern - wie der 8-fache Unterschied in den Laufzeiten zeigt.

Zum Spaß, da Sie bereits wissen, die SQL, versuchen Sie es mit einem direkten DBAPI-Modul in Python, um es zu tun und Laufzeiten zu vergleichen.