OperationalError:数据库已锁定

dan*_*ana 48 python database sqlite django locked

我在我的应用程序中进行了一些重复操作(测试它),突然间我得到一个奇怪的错误:

OperationalError: database is locked
Run Code Online (Sandbox Code Playgroud)

我重新启动了服务器,但错误仍然存​​在.一切都是关于什么的?

pat*_*ick 66

来自django doc:

SQLite意味着是一个轻量级数据库,因此不能支持高级别的并发性.OperationalError:数据库已锁定错误表示您的应用程序正在经历比sqlite在默认配置中可以处理的更多并发性.此错误意味着一个线程或进程对数据库连接具有独占锁定,另一个线程超时等待释放锁定.

Python的SQLite包装器有一个默认的超时值,它确定第二个线程在超时之前允许等待锁的时间长度并引发OperationalError:数据库被锁定错误.

如果您收到此错误,可以通过以下方式解决:

切换到另一个数据库后端.在某个时刻,SQLite对于真实世界的应用程序来说太"精简"了,而这些并发错误表明你已经达到了这一点.

重写代码以减少并发性并确保数据库事务是短暂的.

通过设置timeout database选项optionoption来增加默认超时值

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

  • 指定一个超过默认值的超时可能有助于解决问题:`create_engine('sqlite:/// {}'.format(xxx),connect_args = {'timeout':15})` (3认同)
  • 如果没有额外的澄清,这是一个糟糕的答案。Sqlite 对于绝大多数本地存储使用甚至对于拥有数百个访问者的小型网站来说都非常强大。Basj 的回答对大多数人来说更相关。 (3认同)
  • @ kawing-chiu:您如何运行Django测试呢? (2认同)
  • 来自同一进程上不同线程的两个并发事务都尝试写入数据库,其并发性比 sqlite 可以处理的要高。我的下面的回答对此有更多详细信息。 (2认同)

Wit*_*ail 27

实际的原因通常是python或django shell已经向DB打开了一个请求并且没有正确关闭; 杀死你的终端访问通常会释放它.我今天在运行命令行测试时遇到此错误.

编辑:我定期对此进行投票.如果您想在不重新启动终端的情况下终止访问,那么从命令行可以执行以下操作:

from django import db
db.connections.close_all()
Run Code Online (Sandbox Code Playgroud)

  • 我必须在db函数调用之前设置DJANGO_SETTINGS_MODULE:`os.environ.setdefault("DJANGO_SETTINGS_MODULE","<subfolder_with_setings.json> .settings")`否则,恕我直言最佳答案在这里 (2认同)

Ami*_*ini 18

在我的情况下,这是因为我从SQLite浏览器打开数据库.当我从浏览器关闭它时,问题就消失了.

  • 是的,这对我来说也非常有效。我猜数据库浏览器一定是建立了额外的连接,导致它崩溃。 (5认同)
  • 我通过 SQLite 的数据库浏览器向表添加了一列,它已锁定数据库。关闭它为我解决了这个问题。 (2认同)

Bas*_*asj 13

我不同意@Patrick的回答,该回答通过引用此文档,将OP的问题(Database is locked)隐式链接到此:

切换到另一个数据库后端。在某些时候,SQLite对于实际的应用程序来说太“精简”了,而这些并发错误表明您已经达到了这一点。

将此问题归咎于SQlite有点“太容易”(正确使用时功能非常强大;它不仅是小型数据库的玩具,而且很有趣:)An SQLite database is limited in size to 140 terabytes

除非您有一个非常繁忙的服务器同时具有成千上万的连接,否则Database is locked错误的原因可能是API的错误使用,而不是SQlite固有的问题“太轻”。这里是有关SQLite实现限制的更多信息。


现在的解决方案:

当我同时使用两个脚本使用同一数据库时,我遇到了相同的问题:

  • 一个是通过写操作访问数据库
  • 另一个以只读方式访问数据库

解决方案:始终在执行cursor.close()(甚至是只读)查询后尽快执行。

这里有更多细节

  • 谢谢:如果没有额外的说明,最重要的答案绝对是可怕的:你答案的第一部分很好地涵盖了它。SQlite 对于绝大多数本地存储使用情况来说都非常强大。即使对于拥有数百个访问者的小型网站来说,也可能不值得进一步这样做。 (3认同)
  • 嘿,我在 django 中遇到此错误,其中 django 处理所有数据库查询。有没有办法在 django 中手动关闭光标? (3认同)
  • @evan sqlite 有一个“繁忙超时”。如果将其设置为非零,即使许多线程正在访问数据库,您也永远不会看到此消息......除非这些线程无法关闭事务。保持事务和连接打开会杀死 sqlite“并发” (2认同)
  • 很高兴你写了这个答案,我正要写,但发现你已经提供了这个反馈,我来到这里是因为我面临这个错误,我有预感我的代码有问题而不是 sqlite,我发现为真(固定)。我在基于 REST 的自定义 .net 应用程序服务器后面的单个 sqlite 数据库上运行一个非常繁忙的任务关键型仓库已有 4 年了,从来没有出现过问题(1 个表甚至有大约 100 万行)。人们很快就拒绝了 sqlite,如果可以的话,我会在超级计算机上运行这个该死的数据库。 (2认同)

mrt*_*rts 7

正如其他人所说,还有另一个进程正在使用 SQLite 文件并且尚未关闭连接。如果您使用的是 Linux,您可以db.sqlite3使用以下fuser命令查看哪些进程正在使用该文件(例如):

$ sudo fuser -v db.sqlite3
                     USER        PID ACCESS COMMAND
/path/to/db.sqlite3:
                     user        955 F....  apache2
Run Code Online (Sandbox Code Playgroud)

如果要停止进程以释放锁,请使用fuser -kwhich 将KILL信号发送到访问该文件的所有进程:

sudo fuser -k db.sqlite3
Run Code Online (Sandbox Code Playgroud)

请注意,这很危险,因为它可能会停止生产服务器中的 Web 服务器进程。

感谢@cz-game 指出fuser

  • 这很好用,谢谢:) 在我的情况下`sudo fuser -k app.db` (2认同)

Eva*_*van 7

我在帕特里克的答案中链接的帮助信息未(明确)解决的情况下遇到了此错误消息。

当我过去常常从两个不同的线程transaction.atomic()包装调用FooModel.objects.get_or_create()并同时调用该代码时,只有一个线程会成功,而另一个线程会收到“数据库已锁定”错误。更改超时数据库选项对行为没有影响。

我认为这是因为 sqlite无法处理多个并发写入器,因此应用程序必须自行序列化写入。

threading.RLock我通过使用对象而不是transaction.atomic()当我的 Django 应用程序与 sqlite 后端运行时解决了这个问题。这并不完全等效,因此您可能需要在应用程序中执行其他操作。

这是我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())
Run Code Online (Sandbox Code Playgroud)


Joe*_*Joe 7

当使用 WSL (\\wsl$ ...) 下保存的数据库文件并运行 Windows python 解释器时,我收到此错误。

您可以不将数据库保存在 WSL 树中,也可以在发行版中使用基于 Linux 的解释器。

  • 这也对我有用,将 sqlite 文件从 WSL 复制到 Windows 目录,然后它开始工作。 (2认同)

Shi*_*pak 6

我在 Flask 应用程序中遇到了这个问题,因为我在 SQLite 浏览器中打开数据库并忘记写入更改。

如果您还在 SQLite 浏览器中进行了任何更改,那么单击“写入更改”,一切都会好起来的

在此输入图像描述