如果数据库被锁定,最简单的方法是重试SQLite查询?

时间:2013-02-28 19:19:06

标签: python sqlite

我不太确定在哪里问,我希望它就在这里。

我搜索的是在数据库繁忙时重试SQLite查询的最简单的解决方案。 我在服务器上使用quassel作为我的IRC客户端,并且我想将旧日志移动到单独的DB以保持它使用的小日志。 我写的脚本是:

CREATE TEMP TABLE delfrom (id integer,val integer);
ATTACH '/home/irc/oldlog.db' as log;
BEGIN IMMEDIATE;
REPLACE INTO delfrom (id,val) select 1337,messageid from backlog where time < strftime('%s', 'now','-14 days') ORDER BY messageid DESC LIMIT 1;
INSERT INTO log.log (messageid,time,bufferid,type,flags,senderid,message) SELECT messageid,time,bufferid,type,flags,senderid,message FROM backlog WHERE messageid < (SELECT val FROM delfrom where id=1337);
DELETE FROM backlog WHERE messageid < (SELECT val FROM delfrom where id=1337);
PRAGMA incremental_vacuum;
COMMIT;

我使用sqlite3 quassel-storage.sqlite&lt;运行它movelog.sql

问题是,由于quassel在执行时正在运行,有时BEGIN IMMEDIATE;会因为数据库被锁定而失败。

有人可以建议我一个简单的方法来更改该设置,以便每隔几秒重试一次查询,直到它有效吗? 我读到python SQLite包装器内置了吗?有没有一种特殊的方法我必须激活它,更重要的是,我可以使用python附加第二个数据库吗? sqlite3.connect有一个超时参数,但我不太清楚它是如何工作的。 Python会锁定整个数据库以便在每个连接上写入吗?

我绝不会使用Python。我更喜欢的解决方案是,当发生此错误时sqlite3返回0,然后将其包装在shell中的循环中,但这似乎不起作用。

3 个答案:

答案 0 :(得分:8)

如果被锁定,Python将定期重试。如果数据库被锁定,它将不会重试。表锁只能通过线程,共享连接或其他方法在同一进程内传播。

当多个进程写入文件时会产生数据库锁定,只要日记存在,就会存在(简单地说)。

为避免这种情况,WAL模式可用于日志记录。 (pragma journal_mode = wal;)

要旋转数据库锁,需要使用以下内容包装execute函数:

for x in range(0, timeout):
  try:
    with connection:
      connection.execute(sql)
  except:
     time.sleep(1)
     pass
  finally:
     break
else:
    with connection:
        connection.execute(sql)  

最后一个连接块将使其正确返回异常。这应该通过检查数据库锁定的异常并以其他方式提高原始异常来改进,但这仍然是读者的练习。

答案 1 :(得分:8)

如果版本大于3.7 https://www.sqlite.org/wal.html

,请使用WAL模式进行Sqlite
connect = sqlite3.connect(DB, **kwargs)
connect.execute("PRAGMA journal_mode=WAL")

根据文档&#34; WAL提供更多的并发性,因为读者不会阻止编写者,而编写者不会阻止读者。阅读和写作可以同时进行。&#34;

答案 2 :(得分:6)

如果您已将超时设置得足够高,SQLite库本身将定期重试。

在默认的Python包装器中,这是sqlite3.connect的第二个参数。