SQLite 的 fsync 比你想的贵
往一个表里塞 10 万行,用 executemany 分 1000 批提交。默认配置跑了 40 秒,慢得离谱。
原因
synchronous=FULL(默认)时,每次事务提交要两次 fsync:一次写日志文件,一次写数据库文件。1000 个事务就是 2000 次刷盘。机械盘上单次 fsync 几毫秒,加起来正好几十秒。
三个改法,按效果排序
- 把 pysqlite 的事务控制关掉,自己手动
BEGIN/COMMIT,只用一个大事务。40 秒 → 3 秒。这是最有效的,因为提交次数从 1000 变成 1。 - 开 WAL 模式:
PRAGMA journal_mode=WAL。写入不再阻塞读,提交的 fsync 也从两次变一次。 PRAGMA synchronous=NORMAL:WAL 模式下这是安全的折中,断电最多丢最后一个事务,不会损坏数据库。
代价
synchronous=OFF 我是不敢用的。理论上对上层进程仍然是原子的,但断电时可能把数据库搞成损坏状态。NORMAL 已经够快了,没必要赌。
最后组合是 WAL + NORMAL + 手动大事务,2 秒出头。同一个库,同一个盘。