【问题标题】:'Database is locked' error while using sqlite3 during atomic transactions在原子事务期间使用 sqlite3 时出现“数据库已锁定”错误
【发布时间】:2017-02-15 06:23:44
【问题描述】:

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

【问题讨论】:

    标签: django database sqlite concurrency


    【解决方案1】:

    由于以下两个条件而发生这种情况:

    • 默认情况下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?

    所以你的选择是:

    • 确保首先在事务中获取写锁(即在第一个写查询之前没有任何读查询)。如果可能,您可以通过在事务之外取出读取查询来做到这一点。
    • Monkey-patch 并强制 transaction.atomic() 立即获取写锁,如 btimby 所述:

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

    【讨论】:

    • 我认为遵循最后一个包含很好解释的链接是一个非常好的主意。我是这样修补的。
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-05
      • 1970-01-01
      相关资源
      最近更新 更多