9

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

我搜索的是在数据库繁忙时重试 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 < movelog.sql 运行它

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

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

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

4

3 回答 3

14

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

当多个进程写入文件时会导致数据库锁定,并且(简单地说)只要日志存在就存在。

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

要使用数据库锁,您需要使用以下内容包装执行函数:

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)  

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

于 2014-03-28T09:30:08.117 回答
14

Sqlite 使用 WAL 模式,如果你的版本大于 3.7 https://www.sqlite.org/wal.html

connect = sqlite3.connect(DB, **kwargs)
connect.execute("PRAGMA journal_mode=WAL")

根据文档,“WAL 提供了更多的并发性,因为读取器不会阻塞写入器,写入器不会阻塞读取器。读取和写入可以同时进行。”

于 2016-09-01T07:22:08.610 回答
8

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

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

于 2013-02-28T19:59:50.977 回答