You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WAL模式下SQLite插入5万条记录生成1.5GB WAL文件问题排查

SQLite WAL文件异常膨胀问题排查(插入5万条数据至1.5GB)

核心结论:插入前的DELETE操作不是WAL膨胀的主因

你执行的DELETE FROM fmap仅删除2-3条记录,WAL中只会记录这几条数据的删除日志,数据量极小,完全无法解释1.5GB的WAL膨胀,因此可以排除该操作的影响。

可能的根因分析

1. 单条插入独立提交(最可能)

你的代码中未显式开启事务,SQLite默认处于自动提交模式,每执行一次INSERT OR REPLACE就会创建并提交一个独立事务。WAL对每个独立事务都会写入额外的元数据(事务头、页标记等),5万条独立事务的累积开销会远大于实际数据本身,直接导致WAL文件巨量膨胀。

对比其他表插入正常的情况,大概率是其他表的插入逻辑使用了批量事务(BEGIN后批量插入再COMMIT),避免了单事务的额外开销。

2. max_id值远大于预期

代码中循环次数由max_id + 1决定,如果ixdb_largest_id返回的max_id不是约5万,而是远大于这个数值(比如200万),实际插入的记录数会远超预期,直接导致WAL数据量飙升。建议插入前打印max_id的实际值验证。

3. INSERT OR REPLACE触发不必要的替换逻辑

如果fmap表存在除id外的其他唯一索引,且__fmap_gen生成的数据存在重复的唯一键值,INSERT OR REPLACE会先删除旧记录再插入新记录,每条数据对应两次WAL写入操作,加倍日志量。可检查表结构和生成数据的唯一性确认。

代码修复方案

将所有插入操作包裹在一个事务中,消除单事务的额外开销:

//Insert into DB
stmt_id = XDB_FMAP_INSERT; //INSERT OR REPLACE INTO fmap(id,pid,xt,size,xtime,ytime,selected) values (:id,:pid,:xt,:size,:xtime,:ytime,:selected);
stmt = get_prepared_stmt(xdb, stmt_id); 

// 开启事务
status = sqlite3_exec(xdb->dbh->db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
if (status != 0) {
    goto err;
}

for (uint64_t i = 0; i < max_id + 1; ++i) {
    sqlite3_reset(stmt);
    sqlite3_clear_bindings(stmt);
    sqlite3_bind_int64(stmt, 1, (uint64_t)(i + 1));
    sqlite3_bind_int64(stmt, 2, fmap->hash[i].pid);
    sqlite3_bind_int64(stmt, 3, fmap->hash[i].xtid);
    sqlite3_bind_int64(stmt, 4, fmap->hash[i].size);
    sqlite3_bind_int64(stmt, 5, fmap->hash[i].xtime);
    sqlite3_bind_int64(stmt, 6, fmap->hash[i].ytime);
    sqlite3_bind_int64(stmt, 7, fmap->hash[i].selected);
retry:
    status = sqlite3_step(stmt);
    if (status == SQLITE_BUSY || status == SQLITE_IOERR_BLOCKED) {
        printf("sqlite_step returned: %d . Hence retrying ", status);
        ++retry_count;
        if (retry_count <= ATTEMPTS) { //#define ATTEMPTS 5
            sleep(SLEEP); //#define SLEEP 1
            goto retry;
        }
        printf("Maximum number of retries(%d) exhausted|stmt=%s|err=%d", ATTEMPTS, sqlite3_expanded_sql(stmt), status);
        // 失败时回滚事务
        sqlite3_exec(xdb->dbh->db, "ROLLBACK;", NULL, NULL, NULL);
        goto err;
    }
    else if (status != SQLITE_DONE) {
        printf("\nsqlite3_step() failed with error[%d].\n", status);
        // 失败时回滚事务
        sqlite3_exec(xdb->dbh->db, "ROLLBACK;", NULL, NULL, NULL);
        status = ERR_SQLITE;
        goto err;
    }
    status = 0;
}

// 提交事务
status = sqlite3_exec(xdb->dbh->db, "COMMIT;", NULL, NULL, NULL);
if (status != 0) {
    goto err;
}

if (stmt) {
    sqlite3_reset(stmt);
    sqlite3_clear_bindings(stmt);
}

额外验证步骤

  • 打印max_id的值,确认实际插入记录数是否为预期的5万条。
  • 对比其他表的插入代码,确认是否使用了批量事务逻辑。
  • 执行修改后的代码,监控WAL文件大小,正常情况下应该降到几十MB以内。

内容的提问来源于stack exchange,提问作者rg665n

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 18:55:40