sqlite 减少执行大型事务时的内存消耗

kcwpcxri  于 2023-04-21  发布在  SQLite
关注(0)|答案(1)|浏览(206)

我根据Internet上的一个示例提高了INSERT查询的速度,使用了以下SQLite参数:

PRAGMA journal_mode = OFF;
PRAGMA synchronous = 0;
PRAGMA cache_size = 1000000;
PRAGMA locking_mode = EXCLUSIVE;
PRAGMA temp_store = MEMORY;

下面是我的代码:

tx, err := db.Begin()
if err != nil {
    log.Fatal(err)
}
pr, err := tx.Prepare("INSERT INTO Table (p1, p2, p3, p4, p5) VALUES (?, ?, ?, ?, ?)")
if err != nil {
    log.Fatal(err)
}
defer pr.Close()
for i := 0; i < maxI; i++ {
    for j := 0; j < maxJ; j++ {
        ...
        _, err = pr.Exec(param1, param2, param3, param4, param5)
        if err != nil {
            log.Fatal(err)
        }
    }
}
err = tx.Commit()
if err != nil {
    log.Fatal(err)
}

现在,查询运行得很快,但是消耗了太多的RAM。因为数据存储在RAM中,只有在执行的最后才保存到数据库文件中。
我认为可以周期性地将数据保存到数据库文件中,这会稍微增加执行时间,但减少内存消耗。随着“i”的每一次更改,事务开始,当所有“j”完成时,事务结束:

for i := 0; i < maxI; i++ {
    tx, err := db.Begin()
    if err != nil {
        log.Fatal(err)
    }
    pr, err := tx.Prepare("INSERT INTO Table (p1, p2, p3, p4, p5) VALUES (?, ?, ?, ?, ?)")
    if err != nil {
        log.Fatal(err)
    }
    defer pr.Close()
    for j := 0; j < maxJ; j++ {
        ...
        _, err = pr.Exec(param1, param2, param3, param4, param5)
        if err != nil {
            log.Fatal(err)
        }
    }
    err = tx.Commit()
    if err != nil {
        log.Fatal(err)
    }
}

我认为现在数据应该以块的形式写入文件,并且只有一个数据块应该在RAM中。
但是在执行的过程中,数据并没有保存到文件中,RAM继续被填满。也就是说,第一和第二代码选项在执行上没有区别。
我认为当调用“提交”一个事务时,数据应该保存到一个文件中,RAM应该被清除。请告诉我我做错了什么。

ajsxfq5m

ajsxfq5m1#

PRAGMA cache_size的参数是页面数(通常为每页4k字节)。
PRAGMA cache_size = 1000000;将为页面缓存分配最大4GB的RAM。页面缓存在需要时分配,最大到该最大值,但直到连接关闭才释放。
由于您插入了大量的行,它们最终将位于不同的页面上,因此您最终将缓存已写入磁盘的所有页面,直到填满该高速缓存。
如果你想减少内存消耗,只需将值减少到1000,这相当于4MB,或者完全删除它。默认缓存是2MB,如果你只是插入行,就足够了。
还要注意的是,当你调用COMMIT的时候,数据确实会被写到磁盘上(或者如果没有足够的缓存,甚至在提交之前)。但是Sqlite会在缓存中保留一个副本,以防以后需要它,以避免从磁盘上重新读取它。

相关问题