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 nicht, dass es funktioniert. Das Problem scheint die folgende Zeile zu sein:
conn = sqlite3.connect("mydatabase.db")
Zuvor hatte ich alle meine Ergebnisse in CSV-Dateien gespeichert und hatte keine dieser Dateisperren-Probleme. Hoffentlich wird dies mit sqlite möglich sein. Irgendwelche Ideen?
Sie können ein Consumer-Producer-Muster verwenden. Sie können beispielsweise eine Warteschlange erstellen, die von Threads gemeinsam genutzt wird. Der erste Thread, der Daten aus dem Web abruft, reiht diese Daten in die gemeinsam genutzte Warteschlange ein. Ein anderer Thread, dem die Datenbankverbindung gehört, nimmt die Warteschlange aus der Warteschlange und leitet sie an die Datenbank weiter.
Entgegen der landläufigen Meinung unterstützen neuere Versionen von sqlite3 do den Zugriff von mehreren Threads.
Dies kann über das optionale Schlüsselwortargument check_same_thread
aktiviert werden:
sqlite.connect(":memory:", check_same_thread=False)
Folgendes finden Sie auf mail.python.org.pipermail.1239789
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üsselwort-Argument hinzufügen, und wir können Cursor in einem anderen Thread erstellen. Verwenden Sie also:
sqlite.connect(":memory:", check_same_thread = False)
klappt perfekt für mich. Natürlich muss ich von jetzt an auf den sicheren Multithreading-Zugriff auf die Datenbank achten. Trotzdem alles Gute für den Versuch, zu helfen.
Sie sollten dafür gar keine Threads verwenden. Dies ist eine triviale Aufgabe für twisted und würde Sie wahrscheinlich trotzdem wesentlich weiter bringen.
Verwenden Sie nur einen Thread, und veranlassen Sie den Abschluss der Anforderung, ein Ereignis zum Schreiben auszulösen.
twisted kümmert sich um die Planung, Rückrufe, etc ... für Sie. Es wird Ihnen das gesamte Ergebnis als String übergeben, oder Sie können es über einen Stream-Prozessor ausführen (ich habe eine Twitter-API und eine Friendfeed-API ), die beide Ereignisse als Ergebnis abrufen wird noch heruntergeladen).
Abhängig davon, was Sie mit Ihren Daten tun, können Sie das gesamte Ergebnis einfach in sqlite ablegen, wenn es vollständig ist, es kochen und abspeichern oder es während des Lesens kochen und es am Ende abspeichern.
Ich habe eine sehr einfache Anwendung, die etwas von dem macht, was Sie auf Github wollen. Ich nenne es pfetch (paralleles Holen). Es erfasst verschiedene Seiten nach einem Zeitplan, überträgt die Ergebnisse in eine Datei und führt optional ein Skript aus, wenn die einzelnen Seiten erfolgreich abgeschlossen wurden. Es macht auch ein paar schicke Sachen wie bedingte GETs, aber es könnte trotzdem eine gute Basis für alles sein, was Sie tun.
Wechseln Sie zu Multiprocessing . Es ist viel besser, skaliert gut, kann durch die Verwendung mehrerer CPUs über die Verwendung mehrerer Kerne hinausgehen, und die Schnittstelle entspricht der Verwendung des Python-Threading-Moduls.
Oder verwenden Sie, wie von ALi vorgeschlagen, einfach den Threadpooling-Mechanismus SQLAlchemy . Es wird alles automatisch für Sie erledigen und verfügt über viele zusätzliche Funktionen, um nur einige zu nennen:
Oder wenn Sie wie ich faul sind, können Sie SQLAlchemy verwenden. Es wird das Threading für Sie erledigen ( mit Thread-Local und etwas Verbindungspooling ) und die Art und Weise, wie es ausgeführt wird, ist sogar konfigurierbar .
Wenn Sie außerdem feststellen, dass die Verwendung von Sqlite für eine gleichzeitige Anwendung ein Desaster ist, müssen Sie Ihren Code nicht ändern, um MySQL, Postgres oder irgendetwas anderes zu verwenden. Sie können einfach umschalten.
Sie müssen session.close()
nach jeder Transaktion für die Datenbank verwenden, um denselben Cursor in demselben Thread zu verwenden, der nicht in Multi-Threads den gleichen Cursor verwendet, der diesen Fehler verursacht.
Ich mag Evgenys Antwort - Warteschlangen sind im Allgemeinen die beste Möglichkeit, Inter-Thread-Kommunikation zu implementieren. Der Vollständigkeit halber sind hier einige weitere Optionen aufgeführt:
OperationalError
korrigieren, aber das Öffnen und Schließen von Verbindungen ist aufgrund des Leistungsaufwands im Allgemeinen ein Nein.Verwenden Sie threading.Lock ()
Scrapy scheint eine mögliche Antwort auf meine Frage zu sein. Ihre Homepage beschreibt meine genaue Aufgabe. (Obwohl ich nicht sicher bin, wie stabil der Code noch ist.)
Sie müssen die Parallelität für Ihr Programm entwerfen. SQLite hat klare Einschränkungen, und Sie müssen diese befolgen, siehe FAQ (auch folgende Frage).
Ich konnte in keiner der obigen Antworten Benchmarks finden, also schrieb ich einen Test, um alles zu vergleichen.
Ich habe 3 Ansätze ausprobiert
Die Ergebnisse und Ergebnisse der Benchmark lauten wie folgt
Den Code und die vollständige Lösung für die Benchmarks finden Sie in meiner SO Antwort HIER Hoffe das hilft!
Ich würde mir das y_serial Python-Modul für Datenpersistenz ansehen: http://yserial.sourceforge.net
behandelt Deadlock-Probleme in Bezug auf eine einzelne SQLite-Datenbank. Wenn die Nachfrage nach Parallelität stark wird, kann die Klasse Farm vieler Datenbanken problemlos eingerichtet werden, um die Last über die stochastische Zeit zu verteilen.
Ich hoffe, das hilft Ihrem Projekt ... es sollte einfach genug sein, um es in 10 Minuten umzusetzen.