【问题标题】:OperationalError: database is lockedOperationalError:数据库已锁定
【发布时间】:2024-04-29 20:35:01
【问题描述】:

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

OperationalError: database is locked

我已重新启动服务器,但错误仍然存​​在。这到底是怎么回事?

【问题讨论】:

    标签: python django database sqlite locked


    【解决方案1】:

    @Shilp Thapak 的回答是正确的:错误的原因是您没有在运行应用程序之前将手动更改写入 DB Browser for SQLite 中的数据。

    如果您没有在所使用的任何 SQL 客户端中编写更改,您仍然可以创建引擎,但是

    engine.connect()

    将抛出有关数据库被锁定的操作错误。

    您可以通过检查回滚日志的存在来检查您的引擎是否可以连接。回滚日志的默认模式是在事务开始和结束时创建和删除。

    它与你的数据库在同一个目录下,它与数据库文件同名并附加后缀“-journal”。

    如果模式没有改变,在Journal mode in Edit pragmas panel in DB Browser for SQLite

    您可以像这样检查临时文件的存在:

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

    阅读更多关于临时文件here

    因此,无需为此关闭 SQLite 的服务器或数据库浏览器。事实上,只要将所有更改都写入,您可以让多个客户端同时连接到数据库,并且仍然可以同时运行您的应用程序。

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

        检查您的数据库是否在另一个数据库浏览器上打开。

        如果在其他应用程序上打开,则关闭该应用程序并再次运行该程序。

        【讨论】:

          【解决方案4】:

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

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

          【讨论】:

          • 这对我也有用,将 sqlite 文件从 WSL 复制到 Windows 目录并开始工作。
          【解决方案5】:

          尝试在 SQLite 中创建新表时出现此错误,但 session 对象包含未提交(尽管已刷新)的更改。

          请确保:

          1. 在创建新表之前提交会话
          2. 关闭所有会话并在新连接中执行表创建
          3. ...

          【讨论】:

            【解决方案6】:

            实际上我也遇到过同样的问题,当我使用“transaction.atomic() 和 select_for_update()”时,我收到错误消息“操作错误:数据库已锁定”,

            经过多次尝试/搜索/阅读 django 文档, 我从 SQLite 本身发现了问题,它不支持 django DOCs 所说的 select_for_update 方法,请查看以下网址并深入阅读:

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

            ,当我搬到 MySQL 时,一切都很好。

            正如 django DOCs 还说,当数据库超时发生时,“数据库被锁定”可能会发生, 他们建议您通过设置以下选项来更改数据库超时:

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

            最后,我推荐你使用 MySQL/PostgreSQL,即使你是在开发环境中工作。

            我希望这对你有帮助。

            【讨论】:

              【解决方案7】:

              正如其他人所说,有另一个进程正在使用 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 服务器进程。

              感谢@cz-game 指出fuser

              【讨论】:

              • 这很好用,谢谢 :) sudo fuser -k app.db 在我的情况下
              【解决方案8】:

              在我的例子中,我添加了一条手动保存的新记录,然后再次通过 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)>]>
              

              【讨论】:

                【解决方案9】:

                我发现这可以满足我的需求。 (线程锁定)YMMV conn = sqlite3.connect(数据库,超时=10)

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

                sqlite3.connect(database[, timeout, detect_types,isolation_level, check_same_thread, factory, cached_statements, uri])

                当一个数据库被多个连接访问,并且其中一个进程修改了数据库时,SQLite 数据库将被锁定,直到该事务被提交。 timeout 参数指定连接应该等待锁消失多长时间,直到引发异常。超时参数的默认值为 5.0(五秒)。

                【讨论】:

                  【解决方案10】:

                  只需重新启动您的服务器,它将清除所有当前锁定您的数据库的进程。

                  【讨论】:

                  • 这个问题已经有 17 个答案了。您的答案如何为它们添加任何新知识?
                  【解决方案11】:

                  如果您在使用manage.py shell 时遇到此错误,一个可能的原因是您有一个正在运行的开发服务器 (manage.py runserver) 正在锁定数据库。在使用 shell 时停止服务器总是为我解决了这个问题。

                  【讨论】:

                    【解决方案12】:

                    这里已经有很多答案了,即使我想分享我的案例,这可能会对某人有所帮助..

                    我已经在 Python API 中打开了连接以更新值,只有在收到服务器响应后我才会关闭连接。我在这里所做的是在关闭 Python API 中的连接之前,我已经打开了连接以在服务器中执行一些其他操作。

                    【讨论】:

                      【解决方案13】:

                      只需关闭(停止)并打开(启动)数据库。这解决了我的问题。

                      【讨论】:

                        【解决方案14】:

                        一个非常不寻常的场景,发生在我身上。

                        无限递归,不断创建对象。

                        更具体地说,使用 DRF,我在视图中覆盖了 create 方法,我做到了

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

                        【讨论】:

                          【解决方案15】:

                          在第一次实例化 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
                          

                          【讨论】:

                            【解决方案16】:

                            来自 django 文档:

                            SQLite 旨在成为一个轻量级的 数据库,因此不能支持 高并发。 OperationalError:数据库已锁定 错误表明您的应用程序 正在经历比 sqlite 默认可以处理 配置。这个错误意味着 一个线程或进程具有独占性 锁定数据库连接和 另一个线程超时等待 锁将被释放。

                            Python 的 SQLite 包装器有一个默认值 确定多长时间的超时值 允许第二个线程等待 在超时之前锁上 引发 OperationalError: database 被锁定错误。

                            如果您收到此错误,您可以 解决方法:

                            • 切换到另一个数据库后端。在某些时候,SQLite 对于实际应用程序来说变得过于“精简”,而这些并发错误表明您已经达到了这一点。
                            • 重写代码以减少并发并确保数据库事务是短暂的。
                            • 通过设置超时数据库选项来增加默认超时值

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

                            【讨论】:

                            • 指定一个比默认更长的超时时间可能有助于缓解问题:create_engine('sqlite:///{}'.format(xxx), connect_args={'timeout': 15})
                            • @kawing-chiu:你是如何运行 Django 测试的?
                            • 来自同一进程的不同线程的两个并发事务都尝试写入数据库,这比 sqlite 可以处理的并发性更高。我在下面的回答对此有更多详细信息。
                            • 为我工作:使用数据库连接(例如 PyCharm、Shell 等)杀死进程并重新启动
                            • 在没有额外说明的情况下,这是一个糟糕的答案。对于绝大多数本地存储使用,甚至对于拥有数百名访问者的小型网站,Sqlite 都非常强大。 Basj 的回答对大多数人来说更相关。
                            【解决方案17】:

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

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

                            将 SQlite 归咎于这个问题有点“太容易”(正确使用时为 very powerful;它不仅是小型数据库的玩具,有趣的事实:An SQLite database is limited in size to 140 terabytes)。

                            除非你有一个非常繁忙的服务器同时有数千个连接,这个Database is locked 错误的原因可能更多的是对 API 的错误使用,而不是 SQlite 固有的问题,即“太轻了”。这里有更多关于Implementation Limits for SQLite的信息。


                            现在解决办法:

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

                            • 一个人正在使用写操作访问数据库
                            • 另一个以只读方式访问数据库

                            解决方案:在完成(甚至是只读的)查询后,请尽快执行cursor.close()

                            Here are more details.

                            【讨论】:

                            • 几个小时前,我同意你的观点,将 sqlite 归咎于这个问题似乎太容易了。但是现在我知道启动包含写入操作的事务的两个同时连接足以获得“数据库已锁定”。有关详细信息,请参阅我的答案。
                            • @evan sqlite 有一个 "busy timeout" 。如果将其设置为非零,即使有许多线程正在访问数据库,您也永远不会看到此消息……除非这些线程无法关闭事务。保持事务和连接打开会杀死 sqlite“并发”
                            • @python_user 没有尽快关闭(甚至是只读的)游标就是这样一个例子。请参阅答案末尾的“更多详细信息”链接以查看完整说明。
                            • 谢谢:如果没有额外的说明,最重要的答案绝对是可怕的:答案的第一部分很好地涵盖了它。对于绝大多数本地存储使用案例,SQlite 非常强大。即使对于拥有数百名访问者的小型网站,也可能不值得再进一步。
                            • 很高兴你写了这个答案,我正要写,但发现你已经提供了这个反馈,我来到这里因为我遇到了这个错误,我有预感我的代码有问题而不是 sqlite,我发现这是真的(已修复)。我在基于 REST 的自定义 .net 应用程序服务器后面的单个 sqlite 数据库上运行一个非常繁忙的关键任务仓库已有 4 年了,并且从未遇到过问题(一张表甚至有大约百万行)。人们太快放弃 sqlite,如果可以的话,我会在超级计算机上运行这个该死的数据库。
                            【解决方案18】:

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

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

                            我认为这是由于 sqlite cannot handle multiple simultaneous writers,所以应用程序必须自己序列化写入。

                            当我的 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())
                            

                            【讨论】:

                            • sqlite 有一个“忙超时”。如果将其设置为非零,即使有许多线程正在访问数据库,您也永远不会看到此消息...除非这些线程无法关闭事务。保持事务和连接打开会杀死 sqlite“并发”
                            【解决方案19】:

                            我遇到了同样的错误!原因之一是数据库连接未关闭。 因此,请检查未关闭的数据库连接。此外,请在关闭连接之前检查您是否已提交数据库。

                            【讨论】:

                              【解决方案20】:

                              更新 django 2.1.7 版

                              我在使用pytestdjango 时遇到此错误sqlite3.OperationalError: database is locked

                              解决方案:

                              如果我们使用@pytest.mark.django_db 装饰器。它所做的是创建一个in-memory-db 用于测试。

                              命名:file:memorydb_default?mode=memory&amp;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 ... == ....
                              

                              【讨论】:

                              • 你是说内存中的sqlite数据库永远不会引发“数据库被锁定”错误吗?这个答案令人困惑,因为原始问题不涉及pytest,我不知道pytest.mark.django_db 做了什么。 sqlite docs 并不是说​​内存数据库有任何不同的并发约束。
                              【解决方案21】:

                              对我来说,一旦我关闭使用 python manage.py shell 打开的 django shell,它就会得到解决

                              【讨论】:

                                【解决方案22】:

                                如果您通过 pycharm 的 dbbrowser 插件连接到您的 sqlite 数据库,也会发生这种情况。断线就能解决问题

                                【讨论】:

                                  【解决方案23】:

                                  就我而言,我没有保存在 SQLite 浏览器中执行的数据库操作。保存它解决了这个问题。

                                  【讨论】:

                                    【解决方案24】:

                                    试试这个命令:

                                    sudo fuser -k 8000/tcp
                                    

                                    【讨论】:

                                    • -1,投反对票,因为它没有解释这个解决方案的作用和方式,同时也对正在使用的端口做出假设
                                    • 有帮助吗?
                                    【解决方案25】:

                                    造成这种情况的实际原因通常是 python 或 django shell 已经打开了对 DB 的请求,但没有正确关闭;杀死您的终端访问通常可以释放它。我今天在运行命令行测试时遇到了这个错误。

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

                                    from django import db
                                    db.connections.close_all()
                                    

                                    【讨论】:

                                    • 如何在不杀死终端的情况下修复它?有什么想法吗?
                                    • @neuronet 在 shell 中关闭你的连接?
                                    • 我必须在调用 db 函数之前设置 DJANGO_SETTINGS_MODULE:os.environ.setdefault("DJANGO_SETTINGS_MODULE", "&lt;subfolder_with_setings.json&gt;.settings") 否则,恕我直言,这里是最佳答案
                                    • +1 为db.connections.close_all() 提示。在我使用tearDown() 中的清理脚本之前,我一直在寻找可以解锁数据库的东西。这解决了它。谢谢。
                                    • 我不确定这个 sn-p 做了什么并且它没有解决我的问题,但为了运行它而不会出错,我必须从 here 运行 from django.conf import settings settings.configure()。跨度>
                                    【解决方案26】:

                                    就我而言,这是因为我从 SQLite 浏览器打开数据库。当我从浏览器关闭它时,问题就消失了。

                                    【讨论】:

                                    • 是的,这对我来说太神奇了。我猜 DB 浏览器一定是建立了导致它崩溃的额外连接。
                                    • 我在使用数据库浏览器时看到了同样的行为
                                    • 我通过 DB Browser for SQLite 在表中添加了一列,它已锁定数据库。关闭它为我解决了这个问题。