Wie führe ich eine Masseneinfügung oder -aktualisierung mit SQLAlchemy effizient durch?

8

Ich benutze SQLAlchemy mit einem Postgres-Backend, um ein Bulk-Insert-or-update durchzuführen. Um zu versuchen, die Leistung zu verbessern, versuche ich nur einmal alle tausend Zeilen zu committen:

%Vor%

Dies funktioniert jedoch nicht. Es scheint, dass wenn die INSERT fehlschlägt, Dinge in einem seltsamen Zustand bleiben, der das UPDATE verhindert. Wird die Transaktion automatisch zurückgesetzt? Wenn ja, kann dies gestoppt werden? Ich möchte nicht, dass meine gesamte Transaktion im Falle eines Problems zurückgesetzt wird. Aus diesem Grund versuche ich, die Exception überhaupt zu fangen.

Die Fehlermeldung, die ich erhalte, BTW, ist "sqlalchemy.exc.InternalError: (InternalError) die aktuelle Transaktion wird abgebrochen, Befehle werden bis zum Ende des Transaktionsblocks ignoriert", und dies geschieht beim update (). execute () ruf an.

    
mike 25.08.2009, 19:38
quelle

2 Antworten

5

Sie treffen ein seltsames Postgresql-spezifisches Verhalten: Wenn ein Fehler in einer Transaktion auftritt, wird die gesamte Transaktion rückgängig gemacht. Ich halte das für einen Postgres Design Bug; In einigen Fällen benötigt man etwas SQL-Kontorsionismus.

Eine Problemumgehung besteht darin, das UPDATE zuerst durchzuführen. Ermitteln Sie, ob eine Zeile tatsächlich geändert wurde, indem Sie auf cursor.rowcount schauen. Wenn es keine Zeilen geändert hat, war es nicht vorhanden, also das INSERT. (Dies wird schneller, wenn Sie häufiger aktualisieren als Sie einfügen, natürlich.)

Eine andere Problemumgehung besteht in der Verwendung von Sicherungspunkten:

%Vor%

Dies hat ein ernsthaftes Problem für den Produktionsqualitätscode: Sie müssen den Fehler genau erkennen. Vermutlich erwarten Sie eine eindeutige Constraint-Prüfung, aber Sie treffen möglicherweise etwas Unerwartetes und es ist nahezu unmöglich, den erwarteten Fehler zuverlässig vom unerwarteten zu unterscheiden. Wenn dies den Fehlerzustand falsch trifft, führt dies zu obskuren Problemen, bei denen nichts aktualisiert oder eingefügt wird und kein Fehler angezeigt wird. Sei sehr vorsichtig damit. Sie können den Fehlerfall eingrenzen, indem Sie sich den Fehlercode von Postgresql ansehen, um sicherzustellen, dass es sich um den Fehlertyp handelt, den Sie erwarten, aber das potenzielle Problem ist immer noch vorhanden.

Wenn Sie wirklich Batch-Insert-or-update durchführen möchten, möchten Sie tatsächlich viele von ihnen in wenigen Befehlen ausführen, nicht in einem Element pro Befehl. Dies erfordert komplexere SQL: SELECT verschachtelt innerhalb eines INSERT, Filter die richtigen Elemente zum Einfügen und Aktualisieren.

    
Glenn Maynard 25.08.2009, 20:29
quelle
4

Dieser Fehler stammt von PostgreSQL. PostgreSQL erlaubt Ihnen nicht, Befehle in derselben Transaktion auszuführen, wenn ein Befehl einen Fehler erzeugt. Um dies zu beheben, können Sie verschachtelte Transaktionen (implementiert mit SQL-Sicherungspunkten) über conn.begin_nested() verwenden. Hier ist etwas, das funktionieren könnte. Ich machte den Code mit expliziten Verbindungen, faktorisierte den Chunking-Teil und ließ den Code den Kontextmanager verwenden, um Transaktionen korrekt zu verwalten.

%Vor%

Dies wird jedoch aufgrund des verschachtelten Transaktions-Overheads immer noch keine herausragende Leistung haben. Wenn Sie eine bessere Leistung wünschen, versuchen Sie, mit einer Select-Abfrage zu erkennen, welche Zeilen Fehler verursachen, und verwenden Sie ausführbare Unterstützung (Ausführen kann eine Liste von Dicts aufnehmen, wenn alle Inserts dieselben Spalten verwenden). Wenn Sie mit gleichzeitigen Aktualisierungen arbeiten müssen, müssen Sie die Fehlerbehandlung entweder durch wiederholtes oder wiederholtes Einfügen von Einfügungen durchführen.

    
Ants Aasma 25.08.2009 20:17
quelle

Tags und Links