我在我的应用程序中做了一些重复的操作(测试),突然我收到一个奇怪的错误:
OperationalError: database is locked
我已经重新启动服务器,但是错误仍然存在。可能是什么呢?
Answers:
从Django文档中:
SQLite是一个轻量级的数据库,因此不支持高级别的并发性。OperationalError:数据库已锁定错误,表明您的应用程序并发性高于sqlite在默认配置下无法处理的并发性。此错误意味着一个线程或进程在数据库连接上具有排他锁,而另一个线程超时,等待释放该锁。
Python的SQLite包装器具有默认的超时值,该值确定第二个线程在超时之前允许等待锁多长时间,并引发OperationalError:数据库被锁定错误。
如果遇到此错误,可以通过以下方法解决:
- 切换到另一个数据库后端。在某些时候,SQLite对于实际的应用程序来说太“精简”了,这些并发错误表明您已经达到了这一点。
- 重写代码以减少并发性,并确保数据库事务是短暂的。
- 通过设置超时数据库选项来增加默认超时值
http://docs.djangoproject.com/en/dev/ref/databases/#database-is-locked-errorsoption
造成这种情况的实际原因通常是python或django shell已向数据库打开了一个请求,但未正确关闭。终止终端访问通常可以释放它。我今天在运行命令行测试时遇到此错误。
编辑:我对此有定期的投票。如果您想在不重新启动终端的情况下终止访问,则可以从命令行执行以下操作:
from django import db
db.connections.close_all()
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "<subfolder_with_setings.json>.settings")
否则,恕我直言最好的答案在这里
db.connections.close_all()
小费。我一直在寻找可以解锁数据库的东西,然后再使用中的清理脚本tearDown()
。这样就解决了。谢谢。
我不同意@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文件,但尚未关闭连接。如果您使用的是Linux,则可以db.sqlite3
使用以下fuser
命令查看哪些进程正在使用该文件(例如):
$ sudo fuser -v db.sqlite3
USER PID ACCESS COMMAND
/path/to/db.sqlite3:
user 955 F.... apache2
如果要停止释放锁的进程,请使用use命令fuser -k
将KILL
信号发送到所有访问该文件的进程:
sudo fuser -k db.sqlite3
请注意,这样做很危险,因为它可能会停止生产服务器中的Web服务器进程。
感谢@ cz-game指出fuser
!
sudo fuser -k app.db
在我的案例
在帕特里克答案中所链接的帮助信息未(明确)解决的情况下,我遇到了此错误消息。
当我过去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())
对我来说,一旦我关闭了使用 python manage.py 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(数据库,超时= 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(五秒)。
就我而言,我没有保存在SQLite浏览器中执行的数据库操作。保存它可以解决问题。
如果在使用时收到此错误manage.py shell
,则可能的原因是您有一台正在运行(manage.py runserver
)的开发服务器正在锁定数据库。在使用Shell时停止服务器始终可以解决我的问题。
就我而言,我添加了一个手动保存的新记录,然后再次通过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)>]>
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 ... == ....
试试这个命令:
sudo fuser -k 8000/tcp
create_engine('sqlite:///{}'.format(xxx), connect_args={'timeout': 15})