在原子事务期间使用sqlite3时“数据库被锁定”错误

时间:2017-02-15 06:23:45

标签: django database sqlite concurrency

我之前曾观察到,当我绕过原子事务(django)时,sqlite db写入速度明显更快 - 0.3秒没有,而事务处理时间为0.004秒。因此,我在整个项目中应用了交易。奇怪的是,在我这样做后,我开始遇到'数据库已被锁定'错误,这导致我调试它以发现当更新在事务上运行时(让我们称之为更新A)并且当我尝试同时运行另一个更新时(B)在一个事务上然后它立即失败而不等待超时(默认为5秒)。但是当我尝试在没有事务的情况下运行更新B时,它等待A完成然后完成更新。任何人都可以向我提供一个可能的解释,其中不包括删除交易。

2 个答案:

答案 0 :(得分:0)

可以使用PRAGMA busy_timeout设置SQLite的超时。

默认值为零,此设置仅适用于连接(不适用于数据库),因此看起来并非所有连接都获得了5秒。

通过执行PRAGMA确保所有连接都设置了正确的超时。 (五秒钟是危险的;最好使用三十秒。)

答案 1 :(得分:0)

发生这种情况的原因有两个:

  • 默认情况下,transaction.atomic()开始进行DEFERRED事务,因此一开始就没有锁。
  • 您首先在事务内部阅读,然后尝试写 而另一个进程已经在数据库上具有写锁定。

例如:

# no lock is acquired here because it executes BEGIN query which
# defaults to BEGIN DEFERRED
with transaction.atomic():

    # this acquires read lock on DB
    MyModelName.objects.all().first()

    # this tries to change read lock to write lock
    # but fails because another process is holding a write lock
    MyModelName.objects.create(name='Example')

    # "OperationalError: database is locked" is raised here
    # immediately ignoring the timeout

我不完全确定为什么会发生这种情况,但是我发现另一则帖子说这可能是由于僵局造成的:

sqlite3 ignores sqlite3_busy_timeout?

因此,您的选择是:

  • 确保首先在事务内部获取写锁(即在第一个写查询之前没有任何读查询)。您可以通过在事务外进行读取查询来做到这一点。
  • 猴子补丁并强制transaction.atomic()立即获得写锁,如btimby在此处所述:

Fix SQLite "database is locked" problems using "BEGIN IMMEDIATE"