很多人看到"SQLite 开了 WAL 就能扛并发",默认它现在能跟 Postgres 一样接住一堆写请求。这是个误会。WAL 确实治好了老毛病——写堵读、读堵写,但它没打算解决另一件事:同一时刻,SQLite 仍然只允许一个写事务存在。第二个写请求撞上来,不排队,直接收到 SQLITE_BUSY

这不是"配置没调好"的问题,是架构本身的边界。SQLite 能不能上生产,真正要看的是负载:是不是单节点、是不是读多写少、数据量是不是装得下一块盘。这三条成立,你换来的是低延迟;不成立,你要自己扛下备份、故障转移和临时磁盘失效的责任。

速读:WAL 治好了读写打架,没治好写写打架

  • 默认模式下,写堵读、读堵写.WAL 把写操作挪到独立的 .sqlite-wal 文件,读写从此互不阻塞。
  • 但写和写之间没这待遇.两个写事务同时到,一个拿锁,另一个立刻收到 SQLITE_BUSY
  • busy_timeout 让第二个写请求先等一会儿再重试,BEGIN IMMEDIATE 提前占锁、避免两个连接互相干等死锁——都是缓解手段,不是消除写竞争的开关。
  • 写多的场景,单写者上限迟早显形。
WAL 模式:治好了什么,没治好什么 解决了 读写不再互相阻塞 写追加到WAL文件 读照常读主库 并发提升,来自这里 没解决 同一时刻仅一个写事务 写竞争→SQLITE_BUSY busy_timeout只是缓解 写多场景瓶颈显形 并发读写 ≠ 并发写写

配置边界:参数要按负载实测,不是抄一份清单

检查点(checkpoint)把 WAL 内容合并回主库,防止文件无限膨胀。但只要有活跃的长读事务占着旧页面,PASSIVE 检查点就合不进去,WAL 照样涨。journal_size_limit 只能限制文件大小上限,拦不住它涨——真正要处理的是那个不肯结束的长读事务。

synchronous = NORMAL 能省掉每次提交同步磁盘的开销,在 WAL 模式下也不会导致数据库文件损坏。但完整性和持久性是两件事:完整性没问题,持久性会打折扣。服务器一旦崩溃,还没落盘的已提交事务可能丢失。这笔账要提前算,不是"绝对安全"四个字能盖过去的。

mmap 也不等于"整个数据库常驻内存"。它只是把文件映射交给操作系统页缓存去管,数据库大小超过 mmap_size,该从磁盘读的还是要读。

工具复制机制适用场景
Litestream复制 WAL 增量帧单机持续备份、灾难恢复
LiteFS主要基于 FUSE 拦截文件系统操作多副本读扩展,云上是否使用取决于具体平台承诺

两者机制不同,不能都归成"VFS 层"打包讲。云环境下用不用它们,要看具体平台的持久化承诺,不是一律强制。

本地盘换来的低延迟,把备份、故障转移、临时磁盘失效的风险也一并转移给了应用方自己。

两类读者:上线前分别要做什么

后端和基础设施工程师最该问的,不是这篇文章说得对不对,是上线前有没有测过这四件事:

  1. 压测真实写入并发,看 SQLITE_BUSY 出现的频率,而不是理论上会不会出现。
  2. 手写 busy_timeout 重试逻辑,并且真的验证过——SQLite 不保证退避策略,重试节奏要自己设计。
  3. 定期做一次真实的备份恢复演练,而不是只确认备份文件存在。
  4. 监控 WAL 文件大小趋势和检查点执行情况,不要等磁盘写满才发现问题。

评估单机架构的技术负责人,决策点在写入增长曲线,不在今天的 QPS 数字。

信号还能用 SQLite该考虑迁移
写入来源单节点写入需要多地域同时写入
高可用要求可接受分钟级故障切换需要秒级 SLA
数据规模装得进一块本地盘持续增长,单机存不下
写竞争SQLITE_BUSY 偶发频繁出现,重试队列开始堆积

这张表不是选型公式,是筛子。任何一行滑到右边,继续留在 SQLite 上,承担的就不再是"性能取舍",而是"故障半径"。

接下来真正该盯的不是 SQLite 本身的版本更新,是团队自己的写入曲线:写入 QPS 每周涨多少,busy_timeout 重试率有没有抬头,WAL 文件峰值有没有越来越难压平。这三条同时往上走,就是该迁移到 client-server 数据库的信号,不必等到服务整体卡死才反应。

我的判断:这是架构取舍,不是 PRAGMA 点石成金

"天下大势,分久必合,合久必分。"数据库这几十年也是这个套路——大型机时代数据集中一处,client-server 架构把"合"制度化;现在 NVMe 和边缘部署让本地存取变便宜,SQLite 被重新提起,走的是"分"的方向。这个类比不完全一样,今天的推力是硬件成本,不是治乱循环,顶多三成像。但架构在集中和分散之间来回摆,这个规律没变。

低延迟是真的,账也是真的。本地读通常能比经网络访问的数据库快出一个数量级,具体数字取决于硬件和负载,没有放之四海而皆准的基准,不该被当成人人适用的承诺。

SQLite 适合的场景很具体:单节点、读多写少、数据量装得进一块盘。这些条件成立,省掉网络往返换来的低延迟就是实打实的收益。条件不成立——需要跨地域写入、需要强 SLA 的高可用——client-server 数据库仍然是对的工具。

本地跑SQLite,你换到的是什么 更低的读延迟 同进程内存映射,省掉网络往返 单写者约束 同一时刻仅一个写事务,竞争自己扛 持久化责任转移 备份、故障转移、磁盘风险归应用方

先问自己是不是单节点、读多写少、数据量装得下一块盘,再决定要不要为这份低延迟接下运维责任。省下来的每一毫秒,最终都要在这份责任清单里找平。