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

Python 如果数据库被锁定,重试SQLite查询的最简单方法是什么?,python,sqlite,Python,Sqlite,我不太清楚该问哪里,我希望就在这里 我搜索的是在数据库繁忙时重试SQLite查询的最简单解决方案。 我使用quassel作为服务器上的IRC客户端,我希望将旧日志移动到单独的数据库中,以保持它使用的数据库较小。 我写的脚本是: CREATE TEMP TABLE delfrom (id integer,val integer); ATTACH '/home/irc/oldlog.db' as log; BEGIN IMMEDIATE; REPLACE INTO delfrom (id,val)

我不太清楚该问哪里,我希望就在这里

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

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 问题是,由于quassel在执行时正在运行,因此有时会立即开始;失败,因为数据库已锁定

有人能给我一个简单的方法来改变设置,让查询每隔几秒钟重试一次,直到成功吗? 我读到pythonsqlite包装器内置了这个功能?是否有一种特殊的方式来激活它,更重要的是,我可以使用python连接第二个DB吗? sqlite3.connect有一个超时参数,但我不太确定它是如何工作的。Python是否会锁定整个数据库,以便在每个连接上进行写入


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

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


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

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

当多个进程写入文件时,会产生数据库锁定,只要日志存在,数据库就会存在

为了避免这种情况,可以使用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)  

最后一个连接块将使其正确返回异常。这应该通过检查数据库锁定的异常和引发原始异常来改进,但这是留给读者的练习。

如果您的版本大于3.7,请对Sqlite使用WAL模式


根据文档,WAL提供了更多的并发性,因为读卡器不会阻止写入程序,而写入程序也不会阻止读卡器。读取和写入可以同时进行。

在使用sqlite3可执行文件时,重试是否也有效?因为看起来不是!sqlite3可执行文件不使用Python。实际上,虽然设置timeout参数在很多情况下肯定会有帮助,但它并没有按照您的想法工作。它将超时值作为sqlite\u busy\u超时调用发送给sqlite3,后者设置一个busy处理程序。也就是说,是sqlite本身在重试查询,直到超时过期。此外,如果sqlite3检测到对数据库的并发访问处于死锁状态,则不会调用忙碌处理程序。@ChrisCogdon我对SQLite库本身还有什么意思?使用BEGIN IMMEDIATE时不可能出现死锁。应该注意的是。@davidlatwe在这种情况下,消息队列0mq、rabbitmq等的性能会更好。问题,你也试过sshfs吗?@erm3nda抱歉,我没有:有什么理由使用BEGIN in mediate?对于应该在中间开始的提交,稍后重试的目的是什么;,在日记账中的所有其他提交之前正在处理/提交?顺便说一句,这似乎是避免此类提交的数据丢失的好方法。我并不认为有必要在中间使用BEGIN;没有一种方法可以确保在出现故障时重试。理论上,SQlite日志也会尝试这些提交,所以我看不出我们需要如何实现自己的重试循环。
connect = sqlite3.connect(DB, **kwargs)
connect.execute("PRAGMA journal_mode=WAL")