SQLite 开启 WAL 模式:小项目并发锁问题的一次彻底解决

举报
辣椒酱来了 发表于 2026/11/14 06:33:30 2026/11/14
【摘要】 一、问题是怎么出现的单机部署的轻量服务里,SQLite 的性价比几乎无可替代:零运维、单文件、备份即复制。但很多人在并发稍微上来一点之后会撞上同一堆墙——database is locked。我维护的一个 Flask + SQLite 内部库存系统就踩过这个坑:日常只有十几个人用,读请求占九成,写入集中在上午出库时段。在默认的 rollback journal 模式下,写操作要独占整个数据库...

一、问题是怎么出现的


单机部署的轻量服务里,SQLite 的性价比几乎无可替代:零运维、单文件、备份即复制。但很多人在并发稍微上来一点之后会撞上同一堆墙——database is locked。


我维护的一个 Flask + SQLite 内部库存系统就踩过这个坑:日常只有十几个人用,读请求占九成,写入集中在上午出库时段。在默认的 rollback journal 模式下,写操作要独占整个数据库文件,读操作也得等写提交完成。结果就是两个人同时点「出库」,后一个直接报锁超时。


二、WAL 到底改变了什么


WAL(Write-Ahead Logging)的核心变化是:把修改先追加到独立的 -wal 文件,提交时只写日志、不覆盖主库。直接效果有三个。


读不再被写阻塞:读操作看主库加上已提交的 WAL 页快照。写不再被读阻塞:写只往日志尾部追加。只有「写 vs 写」仍然串行。


也就是说,读多写少的业务(绝大多数后台管理系统都属于这类)能拿到接近线性的并发读能力,而写入串行本来就是 SQLite 的设计前提,不会更差。


三、三行 PRAGMA 的取舍


PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA busy_timeout = 5000;


第一条切换日志模式,是持久化设置,写进数据库头,不用每次连接都执行。


第二条把 fsync 从「每次提交」降为「检查点才 fsync」。这是 WAL 下性价比最高的一步:断电最多丢最近若干个已提交事务,但数据库本身不会损坏。如果业务不能接受丢事务,就保留 FULL。


第三条才是很多人忽略的关键。busy_timeout 让连接遇到锁时自动等待指定毫秒再报错,而不是立刻抛异常。把它设成 5 秒之后,我这边「两人同时出库」的报错率从每天几次降到零。


四、三个容易翻车的细节


一是 -wal 和 -shm 是数据库的一部分。备份时只拷 .db 会丢数据,要用 sqlite3 .backup 或 VACUUM INTO,不要直接 cp。


二是不要把库放在 NFS / SMB 共享盘上。WAL 依赖共享内存文件做锁协调,网络文件系统上的锁语义不可靠,会出现难以复现的损坏。


三是 WAL 文件会随写入增长,需要定期 checkpoint。默认 1000 页自动触发,长事务或大批量写入后建议手动跑一次 wal_checkpoint(TRUNCATE),我把它挂在每天凌晨的定时任务里。


五、实测对比


同一台 2 核 4G 的机器,50 并发读加 5 并发写,持续 60 秒:读 P95 延迟从 218 毫秒降到 12 毫秒,写失败率从 3.7% 降到 0,代价是常态多出约 2 MB 的 -wal 文件。数据不算惊艳,但对十几人用的后台系统来说,改动成本只有三行 PRAGMA,收益是彻底消失的锁报错。


六、小结


SQLite 不需要「升级成 PostgreSQL」来解决并发问题,多数时候只是没有打开它本该打开的开关。WAL 模式加上合理的 busy_timeout,再配上正确的备份方式,足够撑起绝大多数单机业务。真正需要换库的信号不是「出现锁」,而是「写入成为瓶颈」——那时候才轮到客户端-服务器架构出场。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。