SQLite WAL模式,后台线程检查点,wal日志永不收缩

4 投票
1 回答
4320 浏览
提问于 2025-04-18 00:03

SQLite的文档提到(在这里)你可以通过在一个单独的线程上运行检查点来避免WAL模式下的暂停。我试过这样做,但似乎没有效果:'-wal'文件不断增大,不清楚是否有数据真的被复制回主数据库文件中,最重要的是,当'-wal'文件变得足够大(超过一GB)时,主线程开始需要等待检查点的完成。

在我的应用中,主线程不断做一些类似于下面的事情,其中generate_data会生成大约一百万行要插入的数据:

db = sqlite3.connect("database.db")
cursor = db.cursor()
cursor.execute("PRAGMA wal_autocheckpoint = 0")
for datum in generate_data():
    # It is a damned shame that there is no way to do this in one operation.
    cursor.execute("SELECT id FROM strings WHERE str = ?", (datum.text,))
    row = cursor.fetchone()
    if row is not None:
        id = row[0]
    else:
        cur.execute("INSERT INTO strings VALUES(NULL, ?)", (datum.text,))
        id = cur.lastrowid
    cursor.execute("INSERT INTO data VALUES (?, ?, ?)",
                   (id, datum.foo, datum.bar))
    batch_size += 1
    if batch_size > batch_limit:
        db.commit()
        batch_size = 0

而检查点线程则执行以下操作:

db = sqlite3.connect("database.db")
cursor = db.cursor()
cursor.execute("PRAGMA wal_autocheckpoint = 0")
while True:
    time.sleep(10)
    cursor.execute("PRAGMA wal_checkpoint(PASSIVE)")

(由于它们在不同的线程中,所以必须使用不同的数据库连接,因为pysqlite不支持多个线程共享一个连接。)切换到FULL或RESTART检查点并没有帮助——这样检查点就会失败。

我该如何让这个真正有效呢?我希望的结果是:1)主线程永远不需要等待,2)日志文件不会无限增长。

1 个回答

5

检查点操作需要锁住整个数据库,这样其他的读写操作就都得被阻止了。
(如果是被动的检查点操作,那就直接中止了。)

所以在一个单独的线程中运行检查点操作并不会提高并发性。
(SQLite的文档之所以这么说,是因为主线程可能没有设计成在空闲时处理检查点操作。)

如果你一直在访问数据库,就无法进行检查点操作。
如果你的批量操作让WAL文件变得太大,你应该在这个循环中插入明确的检查点(或者依赖自动检查点功能)。

撰写回答