Hallo, ich kämpfe seit über einer Woche mit Problemen, die beim Zugriff auf meine SQLite Datebank auftreten.
Erst einmal habe ich verschiedene Threads und aus jedem Thread soll auf die Datenbank zugegriffen werden können. Deshalb habe ich check_same_thread = False gesetzt. Trotzdem kommt es zu "Database is locked" Fehlermeldungen, weshalb ich autocommit durch "isolation_level=None" aktiviert habe.
Der "Database is locked" Fehler trifft dann nicht mehr auf, aber dafür stürzt das Programm aus dem nichts ab. In der Konsole ist dann nurnoch "Speicherzugriffsfehler" zu lesen.
ich kann mir sehr gut vorstellen, dass sich da verschiedene SQLite Zugriffe in die quere kommen, aber weiß einfach nicht wie ich das lösen kann.
Ich hoffe jemand hat eine Idee
SQLite - Database is locked/Speicherzugriffsfehler
Oha, du solltest unbedingt aufhören mittels Raten nach funktionierenden Lösungen zu suchen
Im Prinzip stellst du nur alle (berechtigten) Warnungen ab.
Die Lösung ist an sich ganz einfach: du brauchst ein zentrales Objekt, welches sich alleine um die Datenbank kümmert. Dieses musst du dann an die Threads übergeben und die Threads können nur noch über diese Zwischenschicht mit der Datenbank kommunizieren. Die Schnittstelle dieses Zwischenobjekts sorgt dann selbst dafür, dass alle Zugriffe entsprechend gekapselt werden.

Die Lösung ist an sich ganz einfach: du brauchst ein zentrales Objekt, welches sich alleine um die Datenbank kümmert. Dieses musst du dann an die Threads übergeben und die Threads können nur noch über diese Zwischenschicht mit der Datenbank kommunizieren. Die Schnittstelle dieses Zwischenobjekts sorgt dann selbst dafür, dass alle Zugriffe entsprechend gekapselt werden.
Das Leben ist wie ein Tennisball.
Oder in Kurzform: Auf SQL-Datenbanken kann nicht aus mehreren Threads schreibend zugriffen werden, jedenfalls soweit ich weiß.
Wie EyDu schrieb: sorg für eine zentrale Anlaufstelle für alle Zugriffe auf die Datenbank und sorge selbst dafür, dass immer nur ein Thread zugreift.
Wie EyDu schrieb: sorg für eine zentrale Anlaufstelle für alle Zugriffe auf die Datenbank und sorge selbst dafür, dass immer nur ein Thread zugreift.
- noisefloor
- User
- Beiträge: 4149
- Registriert: Mittwoch 17. Oktober 2007, 21:40
- Wohnort: WW
- Kontaktdaten:
Hallo,
> Auf SQL-Datenbanken kann nicht aus mehreren Threads schreibend zugriffen werden, jedenfalls soweit ich weiß.
Jein. SQLite kann es nicht. Andere DBs schon, weil die sich dann selber intern die Schreibzugriffe in eine Abarbeitungsschlange sortieren.
@Trubinial Guru: Willst du aus mehreren Threads schreiben oder lesen? Du schreibst immer nur von "Zugriff" - was ja beides sein kann.
Gruß, noisefloor
> Auf SQL-Datenbanken kann nicht aus mehreren Threads schreibend zugriffen werden, jedenfalls soweit ich weiß.
Jein. SQLite kann es nicht. Andere DBs schon, weil die sich dann selber intern die Schreibzugriffe in eine Abarbeitungsschlange sortieren.
@Trubinial Guru: Willst du aus mehreren Threads schreiben oder lesen? Du schreibst immer nur von "Zugriff" - was ja beides sein kann.
Gruß, noisefloor
Das ist ja eine ziemlich wilde Ansammlung von Halbwahrheiten hier. Um es freundlich zu sagen...
Fakt ist: mit SQLite kann man sehr wohl mit mehreren Threads gleichzeitig lesen und schreiben. Es ist allerdings nicht besonders optimiert dafuer, parallel zu schreiben, und setzt dort recht grobe Locks. So, wie das Python-GIL...
Und die Aussage von sparrow ist natuerlich kompletter Unfug. Groessere DB-Engines wie zB Postgres skalieren durchaus mit Anzahl der Kerne zB. Das hoert sich eher nach NoSQL-Propaganda an...
Und darum sind die hier gegebenen Tipps, dass man den Zugriff auf die DB serialisieren muesste, auch voellig falsch. Wie folgendes kleines Skript sofort verdeutlicht:
@Trubinalguru: ich vermute dein Problem liegt darin, dass du eine Connection zwischen verschiedenen Threads *teilst*. Das darfst du natuerlich nicht machen, bzw. dann tatsaechlich nur, wenn du die Zugriffe serialisierst. Und das ist im uebrigen auch keine Einschraenkung von SQLite - sowas koennen die wenigen DB-Adapter.
Fakt ist: mit SQLite kann man sehr wohl mit mehreren Threads gleichzeitig lesen und schreiben. Es ist allerdings nicht besonders optimiert dafuer, parallel zu schreiben, und setzt dort recht grobe Locks. So, wie das Python-GIL...
Und die Aussage von sparrow ist natuerlich kompletter Unfug. Groessere DB-Engines wie zB Postgres skalieren durchaus mit Anzahl der Kerne zB. Das hoert sich eher nach NoSQL-Propaganda an...
Und darum sind die hier gegebenen Tipps, dass man den Zugriff auf die DB serialisieren muesste, auch voellig falsch. Wie folgendes kleines Skript sofort verdeutlicht:
Code: Alles auswählen
import sqlite3
import threading
import random
import os
DB = "/tmp/test.db"
if not os.path.exists(DB):
conn = sqlite3.connect(DB)
c = conn.cursor()
#c.execute("""create table test ( foo int)""")
conn.close()
def work():
conn = sqlite3.connect(DB)
c = conn.cursor()
while True:
if random.random() > .5:
v = random.randint(0, 10000)
c.execute("insert into test values (?)", (v,))
else:
c.execute("select count(*) from test")
conn.commit()
threads = []
for _ in xrange(10):
t = threading.Thread(target=work)
t.setDaemon(True)
t.start()
threads.append(t)
while True:
pass
Oh, und jetzt lese ich erst die Anwort von Noisefloor... noch bunter. Und noch falscher. Natuerlich kommt es vor, das Zugriffe serialisiert werden. Aber grosse DB-Engines haben feingranulares locking, welches dafuer sorgt, dass nur dann, wenn es wirklich *konkurrierende* Zugriffe sind, also Zugriffe auf Daten, die in denselben Tabellen bzw. sogar selben Spalten liegen, es zu solchen "harten" Massnahmen kommt.
Wenn Zugriffe disjunkt sind, wird da nix serialisiert...
Wenn Zugriffe disjunkt sind, wird da nix serialisiert...
- noisefloor
- User
- Beiträge: 4149
- Registriert: Mittwoch 17. Oktober 2007, 21:40
- Wohnort: WW
- Kontaktdaten:
Hallo,
@deets: so meinte ich das auch. Dein Erklärung ist aber... besser als meine
Und noch 'ne Frage:
Und eine NoSQL Anmerkung:
Da war auch der Hintergrund der Frage an den OP: Wenn man konkurrierende / parallele Schreibzugriffe hat, dann heißt das für mich implizit, dass a) entweder viel oder b) sehr lang geschrieben wird. Und dann möchte man sich evtl. mal nach anderen DBs umschauen - oder den Code bzw. die Tabellenstruktur überdenken.
Gruß, noisefloor
@deets: so meinte ich das auch. Dein Erklärung ist aber... besser als meine

Und noch 'ne Frage:
Genau, es gib ein globales Lock auf die DB. Aber genau dadurch geht "parallel" doch nicht?Es ist allerdings nicht besonders optimiert dafuer, parallel zu schreiben, und setzt dort recht grobe Locks. So, wie das Python-GIL...
Und eine NoSQL Anmerkung:
Ja und nein. Es gibt ja auch NoSQL-DBs, die das nicht können.Das hoert sich eher nach NoSQL-Propaganda an...
Da war auch der Hintergrund der Frage an den OP: Wenn man konkurrierende / parallele Schreibzugriffe hat, dann heißt das für mich implizit, dass a) entweder viel oder b) sehr lang geschrieben wird. Und dann möchte man sich evtl. mal nach anderen DBs umschauen - oder den Code bzw. die Tabellenstruktur überdenken.
Gruß, noisefloor
Mit Verlaub: deine Erklaerung ist komplett falsch. Bezogen auf SQLite behauptest du, dass man nicht mit mehreren Threads schreibend darauf zugreifen kann. Kann man aber. Und du behauptest weiter, alle *anderen* DBs wuerden Schreibzugriffe zwangsweise serialisieren. Was sie nicht tun.noisefloor hat geschrieben:Hallo,
@deets: so meinte ich das auch. Dein Erklärung ist aber... besser als meine![]()
Das magst du nicht gemeint haben, aber so hast du's nunmal gesagt.
Dass es genau *ein* Lock setzt muss nicht so sein. In Version 3 hat sich da sehr viel getan:Und noch 'ne Frage:Genau, es gib ein globales Lock auf die DB. Aber genau dadurch geht "parallel" doch nicht?Es ist allerdings nicht besonders optimiert dafuer, parallel zu schreiben, und setzt dort recht grobe Locks. So, wie das Python-GIL...
http://www.sqlite.org/lockingv3.html
Was da genau und im Detail wann und wo und im Vergleich zu anderen DBs passiert, kannst du ja selbst rausfinden.
Deine Implikation ist ebenfalls falsch. Konkurrierende Zugriffe hat zB jede Webseite, schon alleine wenn ein einziger Browser mehrere lesende Verbindungen aufmacht. Und die meisten Webseiten da draussen, auch erfolgreiche und grosse, laufen problemlos *ohne* NoSQL-Loesungen.Und eine NoSQL Anmerkung:Ja und nein. Es gibt ja auch NoSQL-DBs, die das nicht können.Das hoert sich eher nach NoSQL-Propaganda an...
Da war auch der Hintergrund der Frage an den OP: Wenn man konkurrierende / parallele Schreibzugriffe hat, dann heißt das für mich implizit, dass a) entweder viel oder b) sehr lang geschrieben wird. Und dann möchte man sich evtl. mal nach anderen DBs umschauen - oder den Code bzw. die Tabellenstruktur überdenken.
Selbst SoundCloud hat bis zu den ersten Millionen Usern noch MySQL eingesetzt... und tut es AFAIK immer noch.
- noisefloor
- User
- Beiträge: 4149
- Registriert: Mittwoch 17. Oktober 2007, 21:40
- Wohnort: WW
- Kontaktdaten:
Hallo,
Abgesehen davon behaupte ich auch nicht, das NoSQL besser ist. Eine weitere Infos vom OP kann man eigentlich noch überhaupt nicht sagen, welche DB "bessere" wäre- und ob überhaupt. Aber die Infos kommen ja (hoffentlich) noch.
Gruß, noisefloor
Lesend ist ja auch selten ein Problem. Deshalb schrieb ich ja auch "schreibend".Konkurrierende Zugriffe hat zB jede Webseite, schon alleine wenn ein einziger Browser mehrere lesende Verbindungen aufmacht. Und die meisten Webseiten da draussen, auch erfolgreiche und grosse, laufen problemlos *ohne* NoSQL-Loesungen.
Abgesehen davon behaupte ich auch nicht, das NoSQL besser ist. Eine weitere Infos vom OP kann man eigentlich noch überhaupt nicht sagen, welche DB "bessere" wäre- und ob überhaupt. Aber die Infos kommen ja (hoffentlich) noch.

Gruß, noisefloor
Und wo habe ich geschrieben, dass ein lesender Browser-Request nicht auch schreibende Operationen ausloest (logging, user-tracking usw)? Und die meisten Webseiten haben ja sogar noch ein Quantum expliziter Schreib-OPs. Facebook zB setzt AFAIK immer noch MySQL ein. Auch wenn die damit nicht 100%ig gluecklich sind, so hat es sie bis zur ersten 100 Million ganz gut getragen...noisefloor hat geschrieben:Lesend ist ja auch selten ein Problem. Deshalb schrieb ich ja auch "schreibend".Konkurrierende Zugriffe hat zB jede Webseite, schon alleine wenn ein einziger Browser mehrere lesende Verbindungen aufmacht. Und die meisten Webseiten da draussen, auch erfolgreiche und grosse, laufen problemlos *ohne* NoSQL-Loesungen.
Tut er? Bei mir nicht. Kann sein, dass du eine alte SQLite verwendest?sparrow hat geschrieben:Huch, ich habe mich oben natürlich verschrieben, es meinte natürlich SQLite Datenbanken.
Der Beispielcode von deets bringt hier auch entsprechend viele "OperationalError: database is locked" Fehler.
Python 2.7.2, das Sqlite 3 kommt da ja mit.deets hat geschrieben:Tut er? Bei mir nicht. Kann sein, dass du eine alte SQLite verwendest?
Auf der Seite der Dokumentation, die du verlinkst, steht das auch ziemlich deutlich:
Man kann das auch ganz gut nachstellen, ganz ohne Threads. Sobald eine Transaktion schreibt bekommt die Datenbank einen exclusiven Lock. Dann kann keine weitere Vebindung schreiben:An EXCLUSIVE lock is needed in order to write to the database file. Only one EXCLUSIVE lock is allowed on the file and no other locks of any kind are allowed to coexist with an EXCLUSIVE lock.
Code: Alles auswählen
Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import sqlite3
>>> DB = r"c:\temp\test.db"
>>> con1 = sqlite3.connect(DB)
>>> con2 = sqlite3.connect(DB)
>>> con1.execute("CREATE TABLE test (nr int, name varchar)")
<sqlite3.Cursor object at 0x02959D20>
>>> con2.execute("SELECT * FROM test")
<sqlite3.Cursor object at 0x02959DE0>
>>> con1.execute("INSERT INTO test (nr, name) VALUES (1, 'hallo')")
<sqlite3.Cursor object at 0x02959E20>
>>> con2.execute("SELECT * FROM test")
<sqlite3.Cursor object at 0x02959DE0>
>>> con1.execute("BEGIN")
<sqlite3.Cursor object at 0x02959D60>
>>> con2.execute("SELECT * FROM test")
<sqlite3.Cursor object at 0x02959DE0>
>>> con1.execute("INSERT INTO test (nr, name) VALUES (2, 'du')")
<sqlite3.Cursor object at 0x02959D60>
>>> con2.execute("SELECT * FROM test")
<sqlite3.Cursor object at 0x02959DE0>
>>> con2.execute("SELECT * FROM test").fetchall()
[(1, u'hallo')]
>>> con2.execute("INSERT INTO test (nr, name) VALUES (3, 'ich')")
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
con2.execute("INSERT INTO test (nr, name) VALUES (3, 'ich')")
OperationalError: database is locked
Zuletzt geändert von sparrow am Freitag 21. September 2012, 12:49, insgesamt 1-mal geändert.
Du hast recht, nach einiger Zeit kam es bei mir tatsaechlich auch zu Write-Locks. Allerdings sehr selten, und so wie es aussieht, haben die auch keine "fatalen" Auswirkungen - man kann die Operation dann wiederholen. Je nach Transaktionsmodus einer Postgres kann einem das dort aber ja auch passieren.
Und eigentlich ist es schon unueblich genug, dass es in Python ueberhaupt passiert - wegen des GIL. Ich habe gerade zb mein Programm hier unten laufen, und seit ca 10minuten kein DB-Lock.
Summa summarum: ich denke nicht, dass der OP mit sqlite ernsthafte Probleme bekommen wird, sofern sein Szenario nicht aussergewoehnlich ist. Schon mein Testprogramm ist ja eher ungewoehnlich mit der hohen Frequenz an Operationen, lesend wie schreibend.
Und eigentlich ist es schon unueblich genug, dass es in Python ueberhaupt passiert - wegen des GIL. Ich habe gerade zb mein Programm hier unten laufen, und seit ca 10minuten kein DB-Lock.
Code: Alles auswählen
import sqlite3
import threading
import random
import os
DB = "/tmp/test.db"
if not os.path.exists(DB):
conn = sqlite3.connect(DB)
c = conn.cursor()
c.execute("""create table test ( foo int)""")
conn.close()
def work():
conn = sqlite3.connect(DB)
c = conn.cursor()
successful_writes = 0
while True:
if random.random() > .5:
if successful_writes % 100 == 0:
print successful_writes
v = random.randint(0, 10000)
while True:
try:
c.execute("insert into test values (?)", (v,))
successful_writes += 1
break
except:
print successful_writes
successful_writes = 0
print "db was locked, retrying"
else:
c.execute("select count(*) from test")
conn.commit()
threads = []
for _ in xrange(10):
t = threading.Thread(target=work)
t.setDaemon(True)
t.start()
threads.append(t)
while True:
pass
In ein Problem läuft man dann, wenn man Transaktionen hat, die unter Umständen länger laufen, und in denen einmal ein Schreibzugriff vorgenommen wurde. Die Transaktion sperrt dann bis zum Ende die _gesamte_ Datenbank. Wenn man das weiß, kann man natürlich entsprechend planen.
- noisefloor
- User
- Beiträge: 4149
- Registriert: Mittwoch 17. Oktober 2007, 21:40
- Wohnort: WW
- Kontaktdaten:
Hallo,
Gruß, noisefloor
Womit wir wieder da sind, was ich oben schon mal gesagt habe: Wir brauchen mehr Infos vom OP.ich denke nicht, dass der OP mit sqlite ernsthafte Probleme bekommen wird, sofern sein Szenario nicht aussergewoehnlich ist.
Gruß, noisefloor
Na mensch, gut, dass wir doch noch was gefunden haben, was du richtig gesagt hast, gell?noisefloor hat geschrieben:Hallo,
Womit wir wieder da sind, was ich oben schon mal gesagt habe: Wir brauchen mehr Infos vom OP.ich denke nicht, dass der OP mit sqlite ernsthafte Probleme bekommen wird, sofern sein Szenario nicht aussergewoehnlich ist.

- noisefloor
- User
- Beiträge: 4149
- Registriert: Mittwoch 17. Oktober 2007, 21:40
- Wohnort: WW
- Kontaktdaten:
Hallo,
genau. Und eigentlich wollte ich genau das von dir bestätigt haben
So, und jetzt poste ich hier nicht mehr, bevor... ihr wisst schon.
Gruss, noisefloor
genau. Und eigentlich wollte ich genau das von dir bestätigt haben

So, und jetzt poste ich hier nicht mehr, bevor... ihr wisst schon.

Gruss, noisefloor
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Kinder, mal etwas runterkommen. Gilt für alle Beteiligten.
Wir sind ja alle auf der selben Seite*.
* Außer natürlich ihr seit jetzt fiese Appleianer oder Android-Fanboys, denn das einzige richtige OS is natürlich Firefox OS. SCNR

* Außer natürlich ihr seit jetzt fiese Appleianer oder Android-Fanboys, denn das einzige richtige OS is natürlich Firefox OS. SCNR
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
-
- User
- Beiträge: 117
- Registriert: Dienstag 7. April 2009, 13:40
Na da hab ich ja was ins rollen gebracht. Vielen Dank schonmal für die Antworten und entschuldigt für die späte antwort. Wie ihr schon richtig erkannt habt, habe ich noch nicht soo viel Erfahrungen mit Datenbanken.
Ich habe jetzt für jeden Thread eigene Verbindungen aufgemacht, aber brauche wohl noch bis morgen um das weiter zu testen.
Meine Datenbank ist jetzt nicht übermäßig groß, aber in den verschiedenen Threads gibt es eben doch nicht nur Lesezugriffe, sondern teilweise auch Schreibzugriffe. SQLite sollte meinen Ansprüchen auf jeden Fall genügen.
Wie wichtig ist es eigentlich die Verbindungen wieder zu schließen? Weil ich bei mir Probleme habe diese zu schließen, da ich sie nicht ausversehen zu früh schließen möchte.
Ich habe jetzt für jeden Thread eigene Verbindungen aufgemacht, aber brauche wohl noch bis morgen um das weiter zu testen.
Meine Datenbank ist jetzt nicht übermäßig groß, aber in den verschiedenen Threads gibt es eben doch nicht nur Lesezugriffe, sondern teilweise auch Schreibzugriffe. SQLite sollte meinen Ansprüchen auf jeden Fall genügen.
Wie wichtig ist es eigentlich die Verbindungen wieder zu schließen? Weil ich bei mir Probleme habe diese zu schließen, da ich sie nicht ausversehen zu früh schließen möchte.