アプリケーションでいくつかの反復操作を行って(テスト)、突然奇妙なエラーが発生します。
OperationalError: database is locked
サーバーを再起動しましたが、エラーは続きます。何がすべてなのでしょうか?
From Django doc:
SQLiteは軽量のデータベースであるため、高レベルの同時実行性をサポートできません。 OperationalError:データベースがロックされているというエラーは、アプリケーションがsqliteがデフォルト構成で処理できるよりも多くの並行性を経験していることを示します。このエラーは、1つのスレッドまたはプロセスがデータベース接続で排他ロックを持ち、別のスレッドがロックの解放を待機してタイムアウトしたことを意味します。
PythonのSQLiteラッパーには、2番目のスレッドがタイムアウトしてOperationalError:データベースがロックされているというエラーが発生するまでにロックを待機できる時間を決定するデフォルトのタイムアウト値があります。
このエラーが発生した場合は、次の方法で解決できます。
別のデータベースバックエンドに切り替えます。特定の時点で、SQLiteは実際のアプリケーションに対して「ライト」になりすぎ、このような種類の同時実行エラーは、その時点に到達したことを示します。
コードを書き換えて、同時実行性を減らし、データベーストランザクションが短命であることを確認します。
タイムアウトデータベースオプションoptionoptionを設定して、デフォルトのタイムアウト値を増やします。
http://docs.djangoproject.com/en/dev/ref/databases/#database-is-locked-errorsoption
この実用的な理由は、多くの場合、pythonまたはDjango=今日はコマンドラインテストの実行中にこのエラーが発生しました。
編集:これについて定期的に賛成票をもらいます。端末を再起動せずにアクセスを強制終了する場合は、コマンドラインから次の操作を実行できます。
from Django import db
db.connections.close_all()
私の場合、SQLite Browserからデータベースを開いたためです。ブラウザから閉じると、問題はなくなりました。
このドキュメントを引用することで、OPの問題(_Database is locked
_)を暗黙的にこれにリンクする@Patrickの答えに同意しません。
別のデータベースバックエンドに切り替えます。特定の時点で、SQLiteは実際のアプリケーションに対して「ライト」になりすぎ、このような種類の同時実行エラーは、その時点に到達したことを示します。
これは、SQliteにこの問題を犯させるのは少し「簡単すぎる」です。同じ秒に数千の接続がある非常にビジーなサーバーがない限り、この_Database is locked
_エラーの理由は、 「明るすぎる」となるSQlite。 SQLiteの実装制限 に関する詳細情報を以下に示します。
今の解決策:
同じデータベースを同時に使用する2つのスクリプトを使用していたときに、同じ問題が発生しました。
解決策:(読み取り専用であっても)クエリを実行した後、常にできるだけ早くcursor.close()
を実行します。
詳細はこちら 。
他の人が言ったように、SQLiteファイルを使用し、接続を閉じていない別のプロセスがあります。 Linuxを使用している場合、次のようにfuser
コマンドを使用して、ファイル(たとえば、db.sqlite3
)を使用しているプロセスを確認できます。
$ Sudo fuser -v db.sqlite3
USER PID ACCESS COMMAND
/path/to/db.sqlite3:
user 955 F.... Apache2
プロセスを停止してロックを解除する場合は、fuser -k
を使用して、ファイルにアクセスするすべてのプロセスにKILL
信号を送信します。
Sudo fuser -k db.sqlite3
本番サーバーでWebサーバープロセスが停止する可能性があるため、これは危険です。
fuser
を指摘してくれた@ cz-gameに感謝します!
私にとっては、Django python manage.py Shell
Patrickの回答にリンクされているヘルプ情報で(明らかに)対処されていない状況で、このエラーメッセージが表示されました。
transaction.atomic()
を使用してFooModel.objects.get_or_create()
への呼び出しをラップし、そのコードを2つの異なるスレッドから同時に呼び出すと、1つのスレッドのみが成功し、もう1つのスレッドは「データベースがロックされました」エラーを取得します。タイムアウトデータベースオプションを変更しても、動作に影響はありませんでした。
これは、sqlite 複数の同時ライターを処理できない であるため、アプリケーションが独自に書き込みをシリアル化する必要があるためだと思います。
私のDjangoアプリがsqliteバックエンドで実行されている場合、transaction.atomic()
の代わりにthreading.RLock
オブジェクトを使用することで問題を解決しました。これは完全に同等ではありません。アプリケーションで何か他のことをする必要があります。
役立つ場合に備えて、2つの異なるスレッドから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())
同じエラーが発生しました!理由の1つは、DB接続が閉じられなかったことです。したがって、閉じられていないDB接続を確認してください。また、接続を閉じる前にcommittedがあるかどうかを確認してください。
これは、pycharmを介してdbbrowserプラグインを介してsqlite dbに接続している場合にも発生する可能性があります。切断すると問題が解決します
私の場合、SQLite Browser内で実行したデータベース操作を保存していませんでした。保存して問題を解決しました。