OperationalError: Datenbank ist gesperrt


74

Ich habe in meiner Anwendung einige sich wiederholende Vorgänge ausgeführt (sie getestet), und plötzlich wird ein seltsamer Fehler angezeigt:

OperationalError: database is locked

Ich habe den Server neu gestartet, aber der Fehler bleibt bestehen. Worum kann es gehen?

Antworten:


94

Aus dem Django-Dokument:

SQLite ist als kompakte Datenbank gedacht und kann daher kein hohes Maß an Parallelität unterstützen. OperationalError: Datenbank ist gesperrt Fehler weisen darauf hin, dass in Ihrer Anwendung mehr Parallelität auftritt, als SQLite in der Standardkonfiguration verarbeiten kann. Dieser Fehler bedeutet, dass ein Thread oder Prozess eine exklusive Sperre für die Datenbankverbindung hat und ein anderer Thread eine Zeitüberschreitung aufweist, die darauf wartet, dass die Sperre aufgehoben wird.

Der SQLite-Wrapper von Python verfügt über einen Standardwert für das Zeitlimit, der bestimmt, wie lange der zweite Thread auf die Sperre warten darf, bevor das Zeitlimit überschritten wird, und den Fehler OperationalError: database is gesperrt auslöst.

Wenn Sie diesen Fehler erhalten, können Sie ihn beheben durch:

  • Wechseln zu einem anderen Datenbank-Backend. Ab einem bestimmten Punkt wird SQLite für reale Anwendungen zu "lite", und diese Art von Parallelitätsfehlern zeigen an, dass Sie diesen Punkt erreicht haben.
  • Schreiben Sie Ihren Code neu, um die Parallelität zu verringern und sicherzustellen, dass Datenbanktransaktionen nur von kurzer Dauer sind.
  • Erhöhen Sie den Standardwert für das Zeitlimit, indem Sie die Option für die Zeitlimitdatenbank festlegen

http://docs.djangoproject.com/de/dev/ref/databases/#database-is-locked-errorsoption


5
Geben Sie eine Zeitüberschreitung an, die länger als die Standardeinstellung ist, um das Problem zu beheben:create_engine('sqlite:///{}'.format(xxx), connect_args={'timeout': 15})
kawing-chiu

2
@ kawing-chiu: Wie machst du das, um Django-Tests durchzuführen?
Frederick Nord

Zwei gleichzeitige Transaktionen von verschiedenen Threads im selben Prozess, die beide versuchen, in die Datenbank zu schreiben, sind paralleler, als SQLite verarbeiten kann. Meine Antwort unten enthält zusätzliche Details dazu.
Evan

Arbeitete für mich: Beenden Sie Prozesse mit einer DB-Verbindung (z. B. PyCharm, Shell usw.) und starten
Sie

35

In meinem Fall lag es daran, dass ich die Datenbank über den SQLite-Browser öffne. Wenn ich es über den Browser schließe, ist das Problem behoben.


Ja, das hat bei mir zu erstaunlich funktioniert. Ich denke, der DB-Browser muss die zusätzliche Verbindung hergestellt haben, die zum Absturz geführt hat.
aaaakshat

35

Der praktische Grund dafür ist häufig, dass die Python- oder Django-Shells eine Anfrage an die DB geöffnet haben und diese nicht ordnungsgemäß geschlossen wurde. Wenn Sie Ihren Terminalzugriff beenden, wird er häufig freigegeben. Ich hatte diesen Fehler beim Ausführen von Befehlszeilentests heute.

Edit: Ich bekomme regelmäßig Upvotes dazu. Wenn Sie den Zugriff beenden möchten, ohne das Terminal neu zu starten, können Sie über die Befehlszeile Folgendes tun:

from django import db
db.connections.close_all()

1
Wie kann man das Problem beheben, ohne das Terminal zu töten? Irgendeine Idee?
Eric

@neuronet schließen Sie Ihre Verbindung in der Shell?
Fast ein Anfänger

2
Ich musste DJANGO_SETTINGS_MODULE vor dem db-Funktionsaufruf einstellen: os.environ.setdefault("DJANGO_SETTINGS_MODULE", "<subfolder_with_setings.json>.settings") Ansonsten IMHO beste Antwort hier
Oliver Zendel

1
+1 für den db.connections.close_all()Tipp. Ich suchte nach etwas, das die Datenbank entsperren würde, bevor ich ein Bereinigungsskript einschalte tearDown(). Dies hat es behoben. Vielen Dank.
Fbicknel

Ich bin mir nicht sicher, was dieses Snippet macht und es hat mein Problem nicht gelöst, aber um es auszuführen, ohne Fehler zu bekommen, musste ich from django.conf import settings settings.configure()von hier aus laufen .
Foad

25

Ich bin mit der Antwort von @ Patrick nicht einverstanden, die durch das Zitieren dieses Dokuments implizit das Problem ( Database is locked) von OP damit verknüpft :

Wechseln zu einem anderen Datenbank-Backend. Ab einem bestimmten Punkt wird SQLite für reale Anwendungen zu "lite", und diese Art von Parallelitätsfehlern zeigen an, dass Sie diesen Punkt erreicht haben.

Dies ist ein bisschen "zu einfach", um SQlite für dieses Problem zu belasten (das bei korrekter Verwendung sehr leistungsfähig ist ; es ist nicht nur ein Spielzeug für kleine Datenbanken, lustige Tatsache :) An SQLite database is limited in size to 140 terabytes.

Wenn Sie keinen sehr ausgelasteten Server mit Tausenden von Verbindungen in derselben Sekunde haben, ist der Grund für diesen Database is lockedFehler wahrscheinlich eher eine schlechte Verwendung der API als ein Problem, das SQlite innewohnt und "zu leicht" wäre . Hier finden Sie weitere Informationen zu Implementierungsbeschränkungen für SQLite .


Nun die Lösung:

Ich hatte das gleiche Problem, als ich zwei Skripte gleichzeitig mit derselben Datenbank verwendete:

  • Einer griff mit Schreibvorgängen auf die Datenbank zu
  • Der andere hat schreibgeschützt auf die Datenbank zugegriffen

Lösung: Führen Sie cursor.close()nach einer (sogar schreibgeschützten) Abfrage immer so bald wie möglich eine Aktion durch.

Hier finden Sie weitere Details .


Vor einigen Stunden stimmte ich Ihnen zu, dass es zu einfach erscheint, SQLite für dieses Problem zu belasten. Aber jetzt weiß ich, dass zwei gleichzeitige Verbindungen, die eine Transaktion starten, die eine Schreiboperation enthält, ausreichen, um "Datenbank ist gesperrt" zu erhalten. Siehe meine Antwort für weitere Details.
Evan

1
@evan sqlite hat eine "ausgelastete Zeitüberschreitung". Wenn Sie einen Wert ungleich Null festlegen, wird diese Meldung auch dann nicht angezeigt, wenn viele Threads auf die Datenbank zugreifen ... es sei denn, diese Threads können eine Transaktion nicht schließen. Das Halten von Transaktionen und Verbindungen tötet SQLite "Parallelität"
Erik Aronesty

8

Wie andere bereits gesagt haben, gibt es einen anderen Prozess, der die SQLite-Datei verwendet und die Verbindung nicht geschlossen hat. Wenn Sie Linux verwenden, können Sie db.sqlite3mit dem folgenden fuserBefehl sehen, welche Prozesse die Datei (zum Beispiel ) verwenden :

$ sudo fuser -v db.sqlite3
                     USER        PID ACCESS COMMAND
/path/to/db.sqlite3:
                     user        955 F....  apache2

Wenn Sie die Prozesse stoppen möchten, um die Sperre aufzuheben, verwenden Sie fuser -kdiese Option, um das KILLSignal an alle Prozesse zu senden, die auf die Datei zugreifen:

sudo fuser -k db.sqlite3

Beachten Sie, dass dies gefährlich ist, da dies den Webserverprozess auf einem Produktionsserver stoppen kann.

Vielen Dank an @ cz-game für den Hinweis fuser!


2
Das funktioniert gut, danke :) sudo fuser -k app.dbin meinem Fall
Tri

3

Ich bin auf diese Fehlermeldung in einer Situation gestoßen, die in den Hilfeinformationen in Patricks Antwort nicht (eindeutig) angesprochen wird.

Wenn ich transaction.atomic()einen Aufruf an FooModel.objects.get_or_create()diesen Code verpackte und ihn gleichzeitig von zwei verschiedenen Threads aus aufrief, war nur ein Thread erfolgreich, während der andere den Fehler "Datenbank ist gesperrt" erhielt. Das Ändern der Timeout-Datenbankoption hatte keine Auswirkungen auf das Verhalten.

Ich denke, dies liegt an der Tatsache, dass SQLite nicht mehrere gleichzeitige Writer verarbeiten kann , sodass die Anwendung Schreibvorgänge selbst serialisieren muss.

Ich habe das Problem gelöst, indem ich ein threading.RLockObjekt verwendet habe, anstatt transaction.atomic()wenn meine Django-App mit einem SQLite-Backend ausgeführt wird. Das ist nicht ganz gleichwertig, daher müssen Sie möglicherweise etwas anderes in Ihrer Anwendung tun.

Hier ist mein Code, der FooModel.objects.get_or_creategleichzeitig von zwei verschiedenen Threads ausgeführt wird, falls es hilfreich ist:

from concurrent.futures import ThreadPoolExecutor

import configurations
configurations.setup()

from django.db import transaction
from submissions.models import ExerciseCollectionSubmission

def makeSubmission(user_id):
    try:
        with transaction.atomic():
            e, _ = ExerciseCollectionSubmission.objects.get_or_create(
                student_id=user_id, exercise_collection_id=172)
    except Exception as e:
        return f'failed: {e}'

    e.delete()

    return 'success'


futures = []

with ThreadPoolExecutor(max_workers=2) as executor:
    futures.append(executor.submit(makeSubmission, 296))
    futures.append(executor.submit(makeSubmission, 297))

for future in futures:
    print(future.result())

SQLite hat eine "ausgelastete Zeitüberschreitung". Wenn Sie einen Wert ungleich Null festlegen, wird diese Meldung auch dann nicht angezeigt, wenn viele Threads auf die Datenbank zugreifen ... es sei denn, diese Threads können eine Transaktion nicht schließen. Das Halten von Transaktionen und Verbindungen tötet SQLite "Parallelität"
Erik Aronesty

2

Dies kann auch passieren, wenn Sie über das DBBrowser-Plugin über Pycharm mit Ihrer SQLite-Datenbank verbunden sind. Das Trennen der Verbindung löst das Problem


2

Für mich wird es gelöst, sobald ich die Django-Shell geschlossen habe, die mit geöffnet wurde python manage.py shell


2

Ich habe den gleichen Fehler! Einer der Gründe war, dass die DB-Verbindung nicht geschlossen wurde. Suchen Sie daher nach nicht geschlossenen DB-Verbindungen . Überprüfen Sie außerdem, ob Sie die Datenbank festgeschrieben haben , bevor Sie die Verbindung schließen.


2

Ich hatte einen ähnlichen Fehler direkt nach der ersten Instanziierung von Django (v3.0.3). Alle Empfehlungen hier funktionierten nicht außer:

  • löschte die db.sqlite3Datei und verlor die Daten dort, falls vorhanden,
  • python manage.py makemigrations
  • python manage.py migrate

Übrigens, wenn Sie nur PostgreSQL testen möchten:

docker run --rm --name django-postgres \
  -e POSTGRES_PASSWORD=mypassword \
  -e PGPORT=5432 \
  -e POSTGRES_DB=myproject \
  -p 5432:5432 \
  postgres:9.6.17-alpine

Ändern Sie das settings.py, um dies hinzuzufügen DATABASES:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'postgres',
        'PASSWORD': 'mypassword',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

... und Datenbankadapter hinzufügen:

pip install psycopg2-binary

Dann das Übliche:

python manage.py makemigrations
python manage.py migrate

0

In meinem Fall hatte ich keine Datenbankoperation gespeichert, die ich im SQLite-Browser ausgeführt hatte. Durch Speichern wurde das Problem behoben.


0

Ein sehr ungewöhnliches Szenario, das mir passiert ist.

Es gab eine unendliche Rekursion, die die Objekte immer wieder schuf.

Insbesondere habe ich mit DRF die Erstellungsmethode in einer Ansicht überschrieben, und das habe ich getan

def create(self, request, *args, **kwargs):
    ....
    ....

    return self.create(request, *args, **kwargs)

0

Schließen Sie einfach die Datenbank (stoppen Sie sie) und öffnen Sie sie (starten Sie sie). Dies löste mein Problem.


0

Hier sind bereits viele Antworten verfügbar, auch wenn ich meinen Fall teilen möchte, kann dies jemandem helfen.

Ich habe die Verbindung in der Python-API geöffnet, um Werte zu aktualisieren. Die Verbindung wird erst nach Erhalt der Serverantwort geschlossen. Hier habe ich die Verbindung geöffnet, um eine andere Operation auf dem Server auszuführen, bevor ich die Verbindung in der Python-API geschlossen habe.


0

Wenn Sie diesen Fehler während der Verwendung erhalten manage.py shell, ist ein möglicher Grund, dass auf einem Entwicklungsserver ( manage.py runserver) die Datenbank gesperrt ist. Das Stoppen des Servers während der Verwendung der Shell hat das Problem für mich immer behoben.


0

Ich fand, dass dies für meine Bedürfnisse funktionierte. (Thread-Sperre) YMMV conn = sqlite3.connect (Datenbank, Timeout = 10)

https://docs.python.org/3/library/sqlite3.html

sqlite3.connect (Datenbank [, Zeitüberschreitung, Erkennungstypen, Isolationsstufe, Prüfname, Factory, zwischengespeicherte Anweisungen, URL])

Wenn auf eine Datenbank über mehrere Verbindungen zugegriffen wird und einer der Prozesse die Datenbank ändert, wird die SQLite-Datenbank gesperrt, bis diese Transaktion festgeschrieben wird. Der Parameter timeout gibt an, wie lange die Verbindung warten soll, bis die Sperre aufgehoben wird, bis eine Ausnahme ausgelöst wird. Der Standardwert für den Timeout-Parameter ist 5.0 (fünf Sekunden).


-1

UPDATE django Version 2.1.7

Ich habe diesen Fehler sqlite3.OperationalError: database is lockedmit pytestmit django.

Lösung:

Wenn wir @pytest.mark.django_dbDekorateur verwenden. Es wird ein in-memory-dbTest erstellt.

Benannt: file:memorydb_default?mode=memory&cache=sharedWir können diesen Namen bekommen mit:

from django.db import connection
db_path = connection.settings_dict['NAME']

Gehen Sie wie folgt vor, um auf diese Datenbank zuzugreifen und sie auch zu bearbeiten:

Stellen Sie eine Verbindung zur Datenbank her:

with sqlite3.connect(db_path, uri=True) as conn:
    c = conn.cursor()

Verwenden Sie uri=Truediese Option, um die Datenträgerdatei anzugeben, die die zu öffnende SQLite-Datenbank ist.

Um den Fehler zu vermeiden, aktivieren Sie Transaktionen im Dekorator:

@pytest.mark.django_db(transaction=True)

Letzte Funktion:

from django.db import connection

@pytest.mark.django_db(transaction=True)
def test_mytest():
    db_path = connection.settings_dict['NAME']
    with sqlite3.connect(db_path, uri=True) as conn:
        c = conn.cursor()
        c.execute('my amazing query')
        conn.commit()
    assert ... == ....

Wollen Sie damit sagen, dass In-Memory-SQLite-Datenbanken niemals den Fehler "Datenbank ist gesperrt" auslösen? Diese Antwort ist verwirrend, weil die ursprüngliche Frage nicht beinhaltet pytestund ich nicht weiß, was pytest.mark.django_dbtut. In den SQLite-Dokumenten wird nicht angegeben, dass für speicherinterne Datenbanken unterschiedliche Einschränkungen für die Parallelität gelten.
Evan

-1

Starten Sie einfach Ihren Server neu, um alle aktuellen Prozesse zu löschen, bei denen Ihre Datenbank gesperrt ist.


1
Es gibt bereits 17 Antworten auf diese Frage. Wie fügt Ihre Antwort ihnen neues Wissen hinzu?
Simas Joneliunas

-7

Versuchen Sie diesen Befehl:

sudo fuser -k 8000/tcp

4
-1 bietet Downvoted da es keine Erklärung, was diese Lösung funktioniert und wie, aber auch Annahmen über den Hafen zu machen , die verwendet wird
helplessKirk

Hat es trotzdem geholfen?
cz Spiel
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.