OperationalError:数据库已锁定


80

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

OperationalError: database is locked

我已经重新启动服务器,但是错误仍然存​​在。可能是什么呢?

Answers:


94

从Django文档中:

SQLite是一个轻量级的数据库,因此不支持高级别的并发性。OperationalError:数据库已锁定错误,表明您的应用程序并发性高于sqlite在默认配置下无法处理的并发性。此错误意味着一个线程或进程在数据库连接上具有排他锁,而另一个线程超时,等待释放该锁。

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

如果遇到此错误,可以通过以下方法解决:

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

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


6
指定比默认长的超时时间可能有助于缓解问题:create_engine('sqlite:///{}'.format(xxx), connect_args={'timeout': 15})
kawing-chiu

2
@ kawing-chiu:您如何运行Django测试呢?
弗雷德里克·诺德

来自同一进程上不同线程的两个并发事务(都试图写入数据库)比并存的并发性多于sqlite可以处理的事务。我在下面的答案对此有更多详细信息。
埃文(Evan)

为我工作:杀死带有数据库连接的进程(例如PyCharm,Shell等)并重新启动
user101

35

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


是的,这对我来说太神奇了。我猜数据库浏览器一定是在进行额外的连接,导致其崩溃。
aaaakshat

35

造成这种情况的实际原因通常是python或django shell已向数据库打开了一个请求,但未正确关闭。终止终端访问通常可以释放它。我今天在运行命令行测试时遇到此错误。

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

from django import db
db.connections.close_all()

1
如何解决而不杀死终端?任何想法?
eric

@neuronet关闭您在shell中的连接?
几乎是初学者

2
我必须设置DJANGO_SETTINGS_MODULE的DB函数调用之前:os.environ.setdefault("DJANGO_SETTINGS_MODULE", "<subfolder_with_setings.json>.settings") 否则,恕我直言最好的答案在这里
奥利弗Zendel

1
+1为db.connections.close_all()小费。我一直在寻找可以解锁数据库的东西,然后再使用中的清理脚本tearDown()。这样就解决了。谢谢。
fbicknel

我不确定此代码段能做什么,也不能解决我的问题,但是为了运行它而不会遇到麻烦,我不得不from django.conf import settings settings.configure()这里开始
Foad

26

我不同意@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归罪于此问题似乎太容易了。但是现在我知道两个同时启动一个包含写操作的事务的连接足以使“数据库被锁定”。请参阅我的答案以获取更多详细信息。
伊万(Evan)

1
@evan sqlite有一个“忙超时”。如果将其设置为非零,即使许多线程正在访问数据库,也永远不会看到此消息...除非这些线程无法关闭事务。持有交易和连接开放杀死sqlite的“并发”
Erik Aronesty 19/09/24

8

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

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

如果要停止释放锁的进程,请使用use命令fuser -kKILL信号发送到所有访问该文件的进程:

sudo fuser -k db.sqlite3

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

感谢@ cz-game指出fuser


2
这工作得很好,谢谢:)sudo fuser -k app.db在我的案例

3

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

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

我认为这是因为sqlite无法处理多个同时进行的写程序,因此应用程序必须自行序列化写程序。

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

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的“并发”
Erik Aronesty 19/09/24

2

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



2

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


2

在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

1

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

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

sqlite3.connect(数据库[,超时,detect_types,isolationation_level,check_same_thread,工厂,cached_statements,uri])

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



0

一个非常不寻常的情况发生在我身上。

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

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

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

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

0

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


0

这里已经有很多答案了,甚至我也想分享我的案例,这可能会对某人有所帮助。

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


0

如果在使用时收到此错误manage.py shell,则可能的原因是您有一台正在运行(manage.py runserver)的开发服务器正在锁定数据库。在使用Shell时停止服务器始终可以解决我的问题。


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

-1

UPDATE Django版本2.1.7

sqlite3.OperationalError: database is locked使用pytest时出现此错误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 ... == ....

您是说内存中的sqlite数据库永远不会引发“数据库已锁定”错误吗?这个答案很混乱,因为原始问题不涉及pytest,我也不知道是什么pytest.mark.django_db。在SQLite的文档不说,在内存数据库有什么不同的并发约束。
埃文(Evan)

-1

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


2
这个问题已经有17个答案。您的答案如何为他们增加任何新知识?
Simas Joneliunas

-7

试试这个命令:

sudo fuser -k 8000/tcp

4
-1,不推荐使用,因为它没有提供此解决方案的作用和方式的解释,同时也对使用的端口进行了假设
helplessKirk

反正有帮助吗?
cz游戏,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.