SQLite WAL 模式踩坑笔记:高并发读写下的几个细节

最近把一个内部服务的存储从 PostgreSQL 换成 SQLite + WAL 模式,跑了一个多月,遇到几个不太显眼但挺关键的坑,记录一下。

为什么用 WAL

默认的 rollback journal 模式在写事务期间会阻塞所有读,对我们这种"读多写少但读不能停"的场景非常不友好。WAL(Write-Ahead Logging)的好处是:

  • 读和写互不阻塞

  • 多个读事务可以并发执行

  • 写性能在多数场景下更好

开启方式很简单:

```sql

PRAGMA journal_mode=WAL;

```

这个 pragma 是持久的,设一次后数据库文件本身的 header 就改了,下次连接不用重设。

坑 1:`-shm` 和 `-wal` 文件不能漏

WAL 模式下数据库会变成三个文件:

```

mydb.sqlite

mydb.sqlite-shm

mydb.sqlite-wal

```

备份脚本如果只复制主文件,恢复出来的数据会是上次 checkpoint 时的状态,**最近的写会全部丢**。正确做法是先调用 `PRAGMA wal_checkpoint(TRUNCATE);` 把 WAL 落盘清空,再复制主文件。或者直接用 SQLite 的在线备份 API(`sqlite3_backup_init`)。

坑 2:`busy_timeout` 必须显式设

WAL 不是万能的。写事务之间还是互斥的,如果两个进程同时尝试写,后来的会立即拿到 `SQLITE_BUSY`。默认 busy handler 直接返回错误,应用层就要写一堆重试逻辑。更好的做法是:

```sql

PRAGMA busy_timeout=5000;

```

这样 SQLite 会在内部等待最多 5 秒,期间持续重试拿写锁。注意这是**每个连接**都要单独设的,不会从某个全局配置继承。

坑 3:长读事务会让 WAL 无限增长

WAL checkpoint 只能 checkpoint 那些已经被所有 reader 看完的页。如果有一个长读事务一直挂着(比如一个忘记关掉的 cursor),WAL 文件会持续增长,永远 checkpoint 不掉。我们生产环境出现过 WAL 涨到 30GB 的情况。

排查方法是查看 `PRAGMA wal_checkpoint(PASSIVE);` 的返回值,第一个数字是 WAL 中的总页数,第二个是已 checkpoint 的页数,第三个是已 checkpoint 但还没复用的页数。如果第二个长期远小于第一个,就说明有 reader 卡住。

坑 4:`synchronous=NORMAL` vs `FULL`

WAL 模式下默认的 `synchronous=FULL` 仍然会在每次提交都 fsync WAL 文件,性能损失比较大。官方文档明确说:在 WAL 模式下用 `NORMAL` 是安全的,崩溃恢复仍然能保证一致性,只是断电瞬间最后那个事务可能丢失。

```sql

PRAGMA synchronous=NORMAL;

```

我们的测试里这个改动让批量插入吞吐量提升了大概 3 倍。

总结

切到 WAL 之后整体延迟和并发表现都好了很多,但 SQLite 的"省心"是建立在你理解它内部行为的前提下的。建议至少把 `journal_mode`、`busy_timeout`、`synchronous`、`wal_autocheckpoint` 这几个 pragma 的语义看一遍再上生产。

相关推荐
ShineWinsu21 分钟前
对于Qt:QTreeWidget常见属性以及QTreeWidgetItem类的解析
开发语言·c++·qt·gui·客户端·多叉树·qtreewidget
落羽的落羽38 分钟前
【C++】C++17的主要特性
linux·服务器·开发语言·c++·算法·机器学习
考虑考虑1 小时前
JDK27中的Set.ofLazy
java·后端·java ee
总要学点什么1 小时前
第108篇:三维特效-图片轨迹线效果——把贴图滚成一串箭头,想用它的本色反而整条消失
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
乌暮2 小时前
Spring AOP 与 Spring 事务:用大白话彻底理解原理与实践
java·数据库·spring boot·后端·sql·学习·spring
我不会起名字3223 小时前
未接入最终读上限的参数:`websocketMaxMsgLen` 与 12.5 倍差距
后端·websocket·go·即时通讯·im·故障排查
一条Go4 小时前
接收方合并去重:同一个人为什么只该收到一次通知
后端·架构·github
张一凡934 小时前
给 Coding Agent 加一道"架构闸门":Jin 是如何做到"先审架构、再写代码、不漂移"的
前端·后端·agent
Jvav练习生4 小时前
AI Agent 总在关键时刻“失忆”翻车?一文讲透上下文工程(附可运行 Python 代码)
后端
leo在掘金4 小时前
只挂了11小时,5000台开发机被塞进SSH公钥
后端·架构