Python 如果数据库被锁定,重试SQLite查询的最简单方法是什么?
我不太清楚该问哪里,我希望就在这里 我搜索的是在数据库繁忙时重试SQLite查询的最简单解决方案。 我使用quassel作为服务器上的IRC客户端,我希望将旧日志移动到单独的数据库中,以保持它使用的数据库较小。 我写的脚本是: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)
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我一点也不热衷于使用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")