peewee 多线程“数据库已锁定”

问题描述 投票:0回答:2

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

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 "\venv\lib\site-packages\peewee.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 "\Python39\lib\concurrent\futures\_base.py", line 438, in result
    return self.__get_result()
  File "\Python39\lib\concurrent\futures\_base.py", line 390, in __get_result
    raise self._exception
  File "\Python39\lib\concurrent\futures\thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
  File "\test.py", line 19, in func
    TestModel.create(number=number)
  File "\venv\lib\site-packages\peewee.py", line 6393, in create
    inst.save(force_insert=True)
  File "\venv\lib\site-packages\peewee.py", line 6603, in save
    pk = self.insert(**field_dict).execute()
  File "\venv\lib\site-packages\peewee.py", line 1911, in inner
    return method(self, database, *args, **kwargs)
  File "\venv\lib\site-packages\peewee.py", line 1982, in execute
    return self._execute(database)
  File "\venv\lib\site-packages\peewee.py", line 2761, in _execute
    return super(Insert, self)._execute(database)
  File "\venv\lib\site-packages\peewee.py", line 2479, in _execute
    cursor = database.execute(self)
  File "\venv\lib\site-packages\peewee.py", line 3173, in execute
    return self.execute_sql(sql, params, commit=commit)
  File "\venv\lib\site-packages\peewee.py", line 3167, in execute_sql
    self.commit()
  File "\venv\lib\site-packages\peewee.py", line 2933, in __exit__
    reraise(new_type, new_type(exc_value, *exc_args), traceback)
  File "\venv\lib\site-packages\peewee.py", line 191, in reraise
    raise value.with_traceback(tb)
  File "\venv\lib\site-packages\peewee.py", line 3160, in execute_sql
    cursor.execute(sql, params or ())
peewee.OperationalError: database is locked

我在

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

我知道 SQLite 允许在一段时间内单个写入器,因此我将所有写入操作放入

db.atomic()
,但由于某些原因,此上下文中的代码会抛出数据库正忙的异常。

我做错了什么?


我知道技术上为什么会发生这种情况。据我了解,当线程执行

.atomic()
调用并且其他线程正在保存数据库进行事务时,它会等待超时并抛出异常。问题是..为什么?它休眠的时间少于一秒,超时设置为 10 秒,因此应该有足够的时间等待数据库被释放并跳转。在实际应用程序事务中,甚至不需要 100 毫秒,但偶尔会发生此异常。


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

sqlite3
,但我不想再为此奋斗了。

python sqlite peewee
2个回答
0
投票

SQLite 本质上是一个单线程数据库。 要允许多线程,您应该使用特定参数初始化 sqlite 螺纹真实。 在这里阅读更多信息:https://www.sqlite.org/threadsafe.html 你可以通过设置 check_same_thread=False 来做到这一点


0
投票

我发现我在关闭数据库连接之前通过打开数据库进行了备份,导致了我这个问题。 在所有数据库都获得 .close() 后,我将备份移至 python 的末尾

“with open(file, 'rb') as f:” - 锁定文件 1/1000 次。

© www.soinside.com 2019 - 2024. All rights reserved.