簡體   English   中英

OperationalError:數據庫被鎖定

[英]OperationalError: database is locked

我在我的應用程序中做了一些重復操作(測試它),突然我得到一個奇怪的錯誤:

OperationalError: database is locked

我已經重新啟動服務器,但錯誤仍然存在。 這到底是怎么回事?

來自 django 文檔:

SQLite 旨在成為輕量級數據庫,因此無法支持高級別的並發。 OperationalError: database is locked 錯誤表明您的應用程序遇到的並發性超出了 sqlite 在默認配置中可以處理的數量。 這個錯誤意味着一個線程或進程在數據庫連接上有一個排他鎖,而另一個線程超時等待鎖被釋放。

Python 的 SQLite 包裝器有一個默認的超時值,它決定了第二個線程在超時之前允許在鎖上等待多長時間並引發 OperationalError: database is locked 錯誤。

如果您收到此錯誤,您可以通過以下方式解決:

  • 切換到另一個數據庫后端。 在某個時刻,SQLite 對於現實世界的應用程序來說變得太“精簡”了,而這些並發錯誤表明您已經達到了這一點。
  • 重寫代碼以減少並發並確保數據庫事務是短暫的。
  • 通過設置 timeout 數據庫選項增加默認超時值

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

就我而言,這是因為我從 SQLite 瀏覽器打開數據庫。 當我從瀏覽器關閉它時,問題就消失了。

我有點不同意接受的答案,通過引用這個文檔,隱含地將 OP 的問題( Database is locked )與此相關聯:

切換到另一個數據庫后端。 在某個時刻,SQLite 對於現實世界的應用程序來說變得太“精簡”了,而這些並發錯誤表明您已經達到了這一點。

這有點“太容易”將 SQlite 歸咎於這個問題(正確使用時非常強大;它不僅是小型數據庫的玩具,有趣的事實: An SQLite database is limited in size to 140 terabytes )。

除非您有一個非常繁忙的服務器,同時有數千個連接,否則Database is locked錯誤的原因可能更多是對 API 的錯誤使用,而不是 SQlite 固有的“太輕”的問題 以下是有關SQLite 實施限制的更多信息。


現在解決方案:

當我同時使用同一個數據庫的兩個腳本時,我遇到了同樣的問題:

  • 一個是通過寫操作訪問數據庫
  • 另一個是以只讀方式訪問數據庫

解決方案:總是在完成(甚至是只讀的)查詢后盡快執行cursor.close()

這里有更多細節

造成這種情況的實際原因通常是 python 或 django shell 已經打開了對 DB 的請求,但沒有正確關閉; 殺死您的終端訪問通常可以釋放它。 我今天在運行命令行測試時遇到了這個錯誤。

編輯:我對此定期進行投票。 如果您想在不重新啟動終端的情況下終止訪問,那么您可以從命令行執行以下操作:

from django import db
db.connections.close_all()

正如其他人所說,還有另一個進程正在使用 SQLite 文件並且沒有關閉連接。 如果您使用的是 Linux,您可以使用fuser命令查看哪些進程正在使用該文件(例如db.sqlite3 ),如下所示:

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

如果要停止進程以釋放鎖,請使用fuser -kKILL信號發送到訪問文件的所有進程:

sudo fuser -k db.sqlite3

請注意,這是危險的,因為它可能會停止生產服務器中的 Web 服務器進程。

感謝@cz-game 指出fuser

在 patrick 的答案中鏈接的幫助信息未(明確)解決的情況下,我遇到了此錯誤消息。

當我使用transaction.atomic()包裝對FooModel.objects.get_or_create()的調用並從兩個不同的線程同時調用該代碼時,只有一個線程會成功,而另一個線程會得到“數據庫已鎖定”錯誤。 更改超時數據庫選項對行為沒有影響。

我認為這是因為 sqlite無法同時處理多個寫入者,因此應用程序必須自己序列化寫入。

當我的 Django 應用程序使用 sqlite 后端運行時,我通過使用threading.RLock對象而不是transaction.atomic()解決了這個問題。 這並不完全等效,因此您可能需要在應用程序中執行其他操作。

這是我從兩個不同線程同時運行FooModel.objects.get_or_create的代碼,以防萬一:

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())

使用保存在 WSL (\\wsl$ ...) 下的數據庫文件並運行 Windows python 解釋器時出現此錯誤。

您可以不將數據庫保存在 WSL 樹中,也可以在發行版中使用基於 linux 的解釋器。

我在我的燒瓶應用程序中遇到了這個問題,因為我在 SQLite 瀏覽器中打開了數據庫並忘記編寫更改。

如果您還在 SQLite 瀏覽器中進行了任何更改,請單擊寫入更改,一切都會好起來的

在此處輸入圖像描述

如果您通過 pycharm 通過 dbbrowser 插件連接到您的 sqlite 數據庫,也會發生這種情況。 斷線就能解決問題

對我來說,一旦我關閉了使用python manage.py shell打開的 django shell,它就會得到解決

我有同樣的錯誤! 原因之一是數據庫連接未關閉。 因此,檢查未關閉的數據庫連接 另外,在關閉連接之前檢查您是否已提交數據庫。

在第一次實例化 Django(v3.0.3)之后,我遇到了類似的錯誤。 這里的所有建議都不起作用,除了:

  • 刪除db.sqlite3文件並丟失那里的數據,如果有的話,
  • python manage.py makemigrations
  • python manage.py migrate

順便說一句,如果你只想測試 PostgreSQL:

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

更改settings.py以添加此DATABASES

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

...並添加數據庫適配器:

pip install psycopg2-binary

然后是通常的:

python manage.py makemigrations
python manage.py migrate

檢查您的數據庫是否在另一個數據庫瀏覽器上打開。

如果它在其他應用程序上打開,則關閉該應用程序並再次運行該程序。

只需關閉(停止)並打開(啟動)數據庫。 這解決了我的問題。

我發現這可以滿足我的需要。 (線程鎖定) YMMV conn = sqlite3.connect(database, timeout=10)

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

sqlite3.connect(數據庫[,超時,detect_types,isolation_level,check_same_thread,工廠,cached_statements,uri])

當一個數據庫被多個連接訪問,並且其中一個進程修改了數據庫時,SQLite 數據庫將被鎖定,直到該事務被提交。 timeout 參數指定連接應該等待鎖消失多長時間,直到引發異常。 超時參數的默認值為 5.0(五秒)。

就我而言,我添加了一條手動保存的新記錄,然后再次通過 shell 嘗試添加新記錄,這次它可以完美地檢查出來。

In [7]: from main.models import Flight

In [8]: f = Flight(origin="Florida", destination="Alaska", duration=10)

In [9]: f.save()

In [10]: Flight.objects.all() 
Out[10]: <QuerySet [<Flight: Flight object (1)>, <Flight: Flight object (2)>, <Flight: Flight object (3)>, <Flight: Flight object (4)>]>

就我而言,我沒有保存在 SQLite 瀏覽器中執行的數據庫操作。 保存它解決了這個問題。

一個非常不尋常的場景,發生在我身上。

有無限遞歸,不斷創建對象。

更具體地說,使用 DRF,我在視圖中覆蓋了 create 方法,我做到了

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

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

這里已經有很多答案了,即使我想分享我的案例,這可能會對某人有所幫助..

我已經在 Python API 中打開了連接以更新值,只有在收到服務器響應后我才會關閉連接。 在這里,我所做的是在關閉 Python API 中的連接之前,我已經打開了連接以在服務器中執行一些其他操作。

如果您在使用manage.py shell時遇到此錯誤,一個可能的原因是您有一個正在運行的開發服務器( manage.py runserver )正在鎖定數據庫。 在使用 shell 時停止服務器總是為我解決了這個問題。

實際上我遇到了同樣的問題,當我使用“transaction.atomic() 和 select_for_update()”時,我收到錯誤消息“OperationalError:數據庫已鎖定”,

經過多次嘗試/搜索/閱讀 django 文檔,我發現 SQLite 本身的問題它不支持 django 文檔所說的 select_for_update 方法,請查看以下 url 並深入閱讀:

https://docs.djangoproject.com/en/dev/ref/databases/#database-is-locked-errors

,當我搬到 MySQL 時,一切都很好。

由於 django DOCs 還說當數據庫超時發生時可能會發生“數據庫被鎖定”,他們建議您通過設置以下選項來更改數據庫超時:

'OPTIONS': {
    # ...
    'timeout': 20,
    # ...
}

最后,我推薦你使用 MySQL/PostgreSQL,即使你是在開發環境中工作。

我希望這對你有幫助。

嘗試在 SQLite 中創建新表時出現此錯誤,但session對象包含未提交(盡管已刷新)的更改。

確保:

  1. 在創建新表之前提交會話
  2. 關閉所有會話並在新連接中執行表創建
  3. ...

@Shilp Thapak的答案是正確的:錯誤的原因是在運行應用程序之前,您沒有在 SQLite 的 DB Browser 中手動更改數據。

如果您沒有在您使用的任何 SQL 客戶端中編寫更改,您仍然可以創建引擎,但是

 engine.connect()

將拋出有關數據庫被鎖定的操作錯誤。

您可以通過檢查回滾日志的存在來檢查您的引擎是否可以連接。 回滾日志的默認模式是在事務開始和結束時創建和刪除。

它存在於您的數據庫所在的同一目錄中,它與數據庫文件具有相同的名稱並附加了后綴“-journal”。

如果模式未更改,則在 DB Browser for SQLite 的 Edit pragmas 面板中的 Journal mode 中

您可以像這樣檢查臨時文件的存在:

 if os.path.isfile('your-database.sqlite-journal'): print("The database is locked. Please write your changes in your SQL client before proceeding.\n")

在此處閱讀有關臨時文件的更多信息。

因此,無需為此關閉 SQLite 的服務器或數據庫瀏覽器。 事實上,只要寫入所有更改,您就可以讓多個客戶端同時連接到數據庫,並且仍然同時運行您的應用程序。

對我來說,這僅僅是因為我在運行我的 Python 代碼以創建新表的同時訪問 SQLite 應用程序中的數據庫。 在代碼完成之前關閉 SQLite 解決了我的問題。

更新django 版本 2.1.7

我收到了這個錯誤sqlite3.OperationalError: database is locked using pytest with django

解決方案:

如果我們使用@pytest.mark.django_db裝飾器。 它所做的是創建一個用於測試in-memory-db

命名為: file:memorydb_default?mode=memory&cache=shared我們可以通過以下方式獲得這個名稱:

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

要訪問此數據庫並對其進行編輯,請執行以下操作:

連接數據庫:

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

使用uri=True指定要打開的 SQLite 數據庫的磁盤文件。

為了避免錯誤激活裝飾器中的事務:

@pytest.mark.django_db(transaction=True)

最終功能:

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 ... == ....

我只需要將alias sqlite='sqlite3'添加到我的~/.zshrc

然后我在~/.pyenv/versions/new-virtualenv中刪除了部分失敗的virtualenv創建並重新運行pyenv virtualenv <name>並且它運行順暢

只需重新啟動服務器,它將清除所有當前鎖定數據庫的進程。

試試這個命令:

sudo fuser -k 8000/tcp

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM