皮尤多线程"database is locked"



我正在开发一款应用程序,它可以在多个线程中工作,并并行执行数据库操作。虽然它通常运行良好,但有时会出现数据库繁忙的异常。我应该说,创建一个紧凑的可复制示例是一项相当复杂的任务,但不知何故,我最终得到了这个:

from concurrent.futures import ThreadPoolExecutor, as_completed
from time import sleep
from random import random
from peewee import SqliteDatabase, Model, FloatField
db = SqliteDatabase("test.db", pragmas={"journal_mode": "wal"}, timeout=10)
class TestModel(Model):
number = FloatField()
class Meta:
database = db
def func(number):
with db.connection_context():
with db.atomic():
TestModel.create(number=number)
sleep(number)
db.create_tables([TestModel])
with ThreadPoolExecutor() as executor:
futures = [executor.submit(func, random()) for _ in range(100)]
for future in as_completed(futures):
result = future.result()

此代码引发一个异常:

Traceback (most recent call last):
File "venvlibsite-packagespeewee.py", line 3160, in execute_sql
cursor.execute(sql, params or ())
sqlite3.OperationalError: database is locked
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test.py", line 33, in <module>
print(future.result())
File "Python39libconcurrentfutures_base.py", line 438, in result
return self.__get_result()
File "Python39libconcurrentfutures_base.py", line 390, in __get_result
raise self._exception
File "Python39libconcurrentfuturesthread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
File "test.py", line 19, in func
TestModel.create(number=number)
File "venvlibsite-packagespeewee.py", line 6393, in create
inst.save(force_insert=True)
File "venvlibsite-packagespeewee.py", line 6603, in save
pk = self.insert(**field_dict).execute()
File "venvlibsite-packagespeewee.py", line 1911, in inner
return method(self, database, *args, **kwargs)
File "venvlibsite-packagespeewee.py", line 1982, in execute
return self._execute(database)
File "venvlibsite-packagespeewee.py", line 2761, in _execute
return super(Insert, self)._execute(database)
File "venvlibsite-packagespeewee.py", line 2479, in _execute
cursor = database.execute(self)
File "venvlibsite-packagespeewee.py", line 3173, in execute
return self.execute_sql(sql, params, commit=commit)
File "venvlibsite-packagespeewee.py", line 3167, in execute_sql
self.commit()
File "venvlibsite-packagespeewee.py", line 2933, in __exit__
reraise(new_type, new_type(exc_value, *exc_args), traceback)
File "venvlibsite-packagespeewee.py", line 191, in reraise
raise value.with_traceback(tb)
File "venvlibsite-packagespeewee.py", line 3160, in execute_sql
cursor.execute(sql, params or ())
peewee.OperationalError: database is locked

我在db.atomic()上下文中添加了sleep()调用,只是为了模拟数据库的一些复杂操作,这需要几百毫秒。

我知道SQLite允许在一段时间内使用单个编写器,所以我已经将所有的编写操作都放入了db.atomic()中,但由于某些原因,该上下文中的代码抛出了数据库繁忙的异常。

我做错了什么?


我知道技术上为什么会发生这种情况。据我所知,当线程执行.atomic()调用,而其他线程持有数据库进行事务处理时,它会等待超时并抛出异常。问题是。。为什么?它的睡眠时间不到一秒,超时设置为10秒,所以应该有足够的时间等待数据库被释放并跳进去。在真实的应用程序中,交易甚至不需要100毫秒,但偶尔会发生这种异常。


此异常在代码中的任何原子更新中随机发生。我甚至添加了日志记录来检查我的任何事务的最长时间,它最长为81ms,超时设置为20秒,每秒最多发生7个事务,所以我不知道它为什么会在内部消亡。切换数据库引擎或切换到低级别的sqlite3可能会更容易,但我不打算再与之斗争了。

SQLite本质上是一个单线程数据库。要允许多线程,您应该使用特定参数初始化sqlite螺纹正确。点击此处阅读更多信息:https://www.sqlite.org/threadsafe.html可以通过设置check_same_thread=False 来完成此操作

最新更新