Ich habe ein Python-Programm, das das "Threading" -Modul verwendet. Einmal pro Sekunde startet mein Programm einen neuen Thread, der einige Daten aus dem Web abruft und diese Daten auf meiner Festplatte speichert. Ich möchte sqlite3 verwenden, um diese Ergebnisse zu speichern, aber ich kann es nicht zum Laufen bringen. Das Problem scheint in der folgenden Zeile zu liegen:
conn = sqlite3.connect("mydatabase.db")
- Wenn ich diese Codezeile in jeden Thread einfüge, wird ein OperationalError angezeigt, der mir mitteilt, dass die Datenbankdatei gesperrt ist. Ich denke, dies bedeutet, dass ein anderer Thread mydatabase.db über eine sqlite3-Verbindung geöffnet und gesperrt hat.
- Wenn ich diese Codezeile in das Hauptprogramm einfüge und das Verbindungsobjekt (conn) an jeden Thread übergebe, erhalte ich einen Programmierfehler, der besagt, dass in einem Thread erstellte SQLite-Objekte nur in demselben Thread verwendet werden können.
Zuvor habe ich alle meine Ergebnisse in CSV-Dateien gespeichert und hatte keine dieser Probleme beim Sperren von Dateien. Hoffentlich wird dies mit SQLite möglich sein. Irgendwelche Ideen?
Antworten:
Sie können das Consumer-Producer-Muster verwenden. Beispielsweise können Sie eine Warteschlange erstellen, die von Threads gemeinsam genutzt wird. Der erste Thread, der Daten aus dem Web abruft, stellt diese Daten in die gemeinsam genutzte Warteschlange. Ein anderer Thread, der eine Datenbankverbindung besitzt, entfernt Daten aus der Warteschlange und leitet sie an die Datenbank weiter.
quelle
Entgegen der landläufigen Meinung, neuere Versionen von sqlite3 tun unterstützen den Zugriff von mehreren Threads.
Dies kann über ein optionales Schlüsselwortargument aktiviert werden
check_same_thread
:quelle
Folgendes auf mail.python.org.pipermail.1239789 gefunden
Ich habe die Lösung gefunden. Ich weiß nicht, warum die Python-Dokumentation kein einziges Wort zu dieser Option enthält. Wir müssen also der Verbindungsfunktion ein neues Schlüsselwortargument hinzufügen, und wir können daraus Cursor in verschiedenen Threads erstellen. Verwenden Sie also:
funktioniert perfekt für mich. Natürlich muss ich mich von nun an um einen sicheren Multithreading-Zugriff auf die Datenbank kümmern. Trotzdem vielen Dank für den Versuch zu helfen.
quelle
Wechseln Sie zu Multiprocessing . Es ist viel besser, lässt sich gut skalieren, kann über die Verwendung mehrerer Kerne hinausgehen, indem mehrere CPUs verwendet werden, und die Schnittstelle entspricht der Verwendung des Python-Threading-Moduls.
Oder verwenden Sie, wie Ali vorgeschlagen hat, einfach den Thread-Pooling-Mechanismus von SQLAlchemy . Es erledigt alles automatisch für Sie und verfügt über viele zusätzliche Funktionen, um nur einige zu nennen:
quelle
Sie sollten dafür überhaupt keine Threads verwenden. Dies ist eine triviale Aufgabe für Twisted und würde Sie wahrscheinlich sowieso erheblich weiter bringen.
Verwenden Sie nur einen Thread und lassen Sie nach Abschluss der Anforderung ein Ereignis auslösen, um den Schreibvorgang durchzuführen.
Twisted kümmert sich um die Planung, Rückrufe usw. für Sie. Sie erhalten das gesamte Ergebnis als Zeichenfolge oder können es über einen Stream-Prozessor ausführen (ich habe eine Twitter-API und eine Friendfeed-API , die beide Ereignisse an Anrufer auslösen , da die Ergebnisse noch heruntergeladen werden).
Je nachdem, was Sie mit Ihren Daten tun, können Sie das vollständige Ergebnis einfach in SQLite speichern, es kochen und sichern oder es während des Lesens kochen und am Ende sichern.
Ich habe eine sehr einfache Anwendung, die etwas in der Nähe von dem macht, was Sie auf Github wollen. Ich nenne es pfetch (paralleles Abrufen). Es erfasst verschiedene Seiten nach einem Zeitplan, überträgt die Ergebnisse in eine Datei und führt optional ein Skript aus, wenn jede Seite erfolgreich abgeschlossen wurde. Es macht auch einige ausgefallene Dinge wie bedingte GETs, könnte aber dennoch eine gute Basis für alles sein, was Sie tun.
quelle
Oder wenn Sie faul sind, wie ich, können Sie SQLAlchemy verwenden . Es übernimmt das Threading für Sie (unter Verwendung des lokalen Threads und einiger Verbindungspools ) und die Art und Weise, wie es ausgeführt wird, ist sogar konfigurierbar .
Wenn Sie feststellen, dass die Verwendung von Sqlite für eine gleichzeitige Anwendung eine Katastrophe darstellt, müssen Sie Ihren Code nicht ändern, um MySQL, Postgres oder etwas anderes zu verwenden. Sie können einfach umschalten.
quelle
Sie müssen
session.close()
nach jeder Transaktion in die Datenbank verwenden, um denselben Cursor im selben Thread zu verwenden, und nicht denselben Cursor in Multithreads, die diesen Fehler verursachen.quelle
Verwenden Sie threading.Lock ()
quelle
Ich mag Evgenys Antwort: Warteschlangen sind im Allgemeinen der beste Weg, um die Kommunikation zwischen Threads zu implementieren. Der Vollständigkeit halber sind hier einige andere Optionen:
OperationalError
, aber das Öffnen und Schließen solcher Verbindungen ist aufgrund des Leistungsaufwands im Allgemeinen ein Nein-Nein.quelle
Sie müssen die Parallelität für Ihr Programm entwerfen. SQLite hat klare Einschränkungen und Sie müssen diese beachten, siehe FAQ (auch die folgende Frage).
quelle
Scrapy scheint eine mögliche Antwort auf meine Frage zu sein. Die Homepage beschreibt meine genaue Aufgabe. (Obwohl ich nicht sicher bin, wie stabil der Code noch ist.)
quelle
Ich würde mir das y_serial Python-Modul für die Datenpersistenz ansehen: http://yserial.sourceforge.net
Hiermit werden Deadlock-Probleme im Zusammenhang mit einer einzelnen SQLite-Datenbank behoben. Wenn die Nachfrage nach Parallelität stark wird, kann die Klassenfarm vieler Datenbanken leicht eingerichtet werden, um die Last über die stochastische Zeit zu verteilen.
Ich hoffe, dies hilft Ihrem Projekt ... es sollte einfach genug sein, um es in 10 Minuten zu implementieren.
quelle
Ich konnte in keiner der obigen Antworten Benchmarks finden, also schrieb ich einen Test, um alles zu bewerten.
Ich habe 3 Ansätze ausprobiert
Die Ergebnisse und Erkenntnisse aus der Benchmark sind wie folgt
Den Code und die Komplettlösung für die Benchmarks finden Sie in meiner SO-Antwort HIER. Hoffe, das hilft!
quelle
Der wahrscheinlichste Grund für Fehler bei gesperrten Datenbanken ist, dass Sie Probleme haben müssen
nach Abschluss einer Datenbankoperation. Wenn Sie dies nicht tun, wird Ihre Datenbank schreibgeschützt und bleibt dies auch. Für die anderen Threads, die auf das Schreiben warten, tritt nach einiger Zeit eine Zeitüberschreitung auf (die Standardeinstellung ist 5 Sekunden, siehe http://docs.python.org/2/library/sqlite3.html#sqlite3.connect für Details dazu). .
Ein Beispiel für eine korrekte und gleichzeitige Einfügung wäre:
Wenn Sie SQLite mögen oder andere Tools haben, die mit SQLite-Datenbanken arbeiten, oder CSV-Dateien durch SQLite-Datenbankdateien ersetzen möchten oder etwas Seltenes wie plattformübergreifendes IPC ausführen müssen, ist SQLite ein großartiges Tool und für diesen Zweck sehr geeignet. Lassen Sie sich nicht unter Druck setzen, eine andere Lösung zu verwenden, wenn sie sich nicht richtig anfühlt!
quelle