未来星球 未来星球
  • 首页
  • 新闻资讯
  • 技术教程
  • 软件工具
  • 知识百科
  • 生活实用
  • 个人随笔
  • 生命外星人
首页 › 技术教程 › 用 SQLite 当生产数据库:WAL 模式、并发上限与备份策略的完整配置
  • 0
  • 0

用 SQLite 当生产数据库:WAL 模式、并发上限与备份策略的完整配置

admin
1 天前
11 views
用 SQLite 当生产数据库:WAL 模式、并发上限与备份策略的完整配置-未来星球

它比你想的能扛,也比你想的更容易被用错

很多人对 SQLite 的印象还停留在「手机 App 里的本地缓存」。实际上,它每天在数以十亿计的设备上承担着真实业务:大部分网站的会话数据、大量桌面软件的项目文件、不少中小型 SaaS 的主库,底层都是它。单个文件、零运维、事务完整,这些特性在小规模场景下比任何服务型数据库都省事。

但「能当生产库」和「直接当生产库用」是两件事。默认配置下的 SQLite 有明确的并发上限,而它的备份方式和 MySQL 完全不同——直接 cp 一个正在写入的库文件,可能得到一个损坏的副本。本文给出一份可以直接落地的配置,并列出四个真实踩过的坑。

1. 先判断你的负载在不在射程内

SQLite 的并发模型很简单:任意时刻只允许一个写者,读者可以有多个。这不是配置问题,是文件锁的物理限制,改不了。所以判断标准不是「访问量大不大」,而是「写操作密不密」。

  • 适合:读多写少,写入可以串行化,单机部署,日写入量在几十万行以内
  • 适合:边缘节点、内部工具、单租户应用、需要随应用一起打包分发的场景
  • 不适合:持续高频写入(如每秒数百次 INSERT),多台机器同时写同一个库
  • 不适合:库文件放在 NFS、SMB 等网络文件系统上——WAL 依赖共享内存,跨网络会直接失效

一个粗略的经验值:写事务每秒个位数到几十次,SQLite 完全无压力;一旦写请求开始排队,P99 延迟会以肉眼可见的方式塌陷,因为每个写者都在等同一把锁。

2. 四个 PRAGMA,决定它稳不稳

SQLite 的默认值偏向「兼容与安全」,不是偏向「生产吞吐」。下面四条是必设项,前三条建议写进连接初始化,第四条按库设置一次即可。

PRAGMA journal_mode = WAL;      -- 持久化设置,写一次即可
PRAGMA synchronous = NORMAL;    -- WAL 下的推荐值
PRAGMA busy_timeout = 5000;     -- 连接级,单位毫秒
PRAGMA foreign_keys = ON;       -- 连接级,默认关闭,必须手动开

journal_mode=WAL 是最关键的一条。回滚日志模式下,写操作会阻塞读;WAL 模式下读写互不阻塞,只有写与写之间互斥。这一条通常能带来数量级的并发改善。

synchronous=NORMAL 在 WAL 模式下是安全的:断电最多丢失最近若干个已提交事务,但不会损坏数据库。如果设成 FULL,每次提交都要 fsync,写入吞吐会掉一个档次;设成 OFF 则是拿完整性换速度,不建议。

busy_timeout 解决的是「锁等待直接报错」的问题。默认值是 0,意味着一旦遇到锁就立刻返回 SQLITE_BUSY。设成 5000 后,连接会自己等最多五秒,绝大多数瞬时竞争都能被消化掉。这一条不设,线上会频繁出现莫名其妙的写失败。

3. 写事务要短,批量写要包起来

WAL 解决了读写互斥,但写者之间仍然串行。所以真正的优化方向只有一个:让每次持锁的时间尽可能短。两条原则。

第一,不要在事务里做网络请求或文件 IO。一个事务里夹了一次 HTTP 调用,锁就被占了几百毫秒,后面所有写者一起排队。正确做法是先在外面把数据准备好,再开事务写入。

第二,批量插入必须显式包在一个事务里。SQLite 默认每条语句自动提交,一千条 INSERT 就是一千次 fsync。包进一个事务后,通常能快两个数量级。

BEGIN IMMEDIATE;
-- 一批 INSERT / UPDATE
COMMIT;

注意用 BEGIN IMMEDIATE 而不是裸 BEGIN。裸 BEGIN 是延迟事务,先拿读锁、写的时候再升级,升级失败会直接返回 SQLITE_BUSY 且 busy_timeout 对它无效;IMMEDIATE 一开始就拿写锁,把等待交给 busy_timeout 处理,行为可预期得多。

4. 备份:不要 cp,用 backup 或 VACUUM INTO

这是最容易出事的一环。数据库正在写入时,磁盘上的 .db 文件处于中间状态,直接复制可能拿到一个页级不一致的文件,而且往往要等到恢复那天才发现。

正确做法有三种,按场景选。

方式 命令 适用场景 注意
在线备份 sqlite3 app.db “.backup ‘/backup/app.db'” 库在线、需要一致性快照 内部用 backup API,可安全并发
导出压缩库 sqlite3 app.db “VACUUM INTO ‘/backup/app.db'” 想要一个紧凑的副本 目标文件必须不存在
停写后复制 先 checkpoint,再 cp 能接受短暂停机 必须同时处理 -wal 与 -shm

日常最省心的是第一种。它不需要停写,也不需要停读,备份过程中数据库照常提供服务,代价只是一点额外的 IO。

如果确实要走「停写后复制」这条路,顺序必须是:先执行 PRAGMA wal_checkpoint(TRUNCATE) 把 WAL 内容合并回主库,再复制 .db 文件,同时确认目录里没有残留的 -wal 文件。只复制 .db 而漏掉 -wal,等于丢掉了最近的写入。

5. WAL 文件会一直长,除非你管它

WAL 模式下会多出两个文件:app.db-wal 和 app.db-shm。前者是预写日志,后者是共享内存索引。正常情况下,WAL 会在达到阈值时自动 checkpoint,把内容写回主库并复用空间。

问题出在长连接上。只要还有一个读事务没结束,checkpoint 就无法推进,WAL 会一直增长。一个忘了提交的读事务,能让 WAL 涨到几个 GB。所以两件事要做:

  • 设置 PRAGMA wal_autocheckpoint = 1000(页数,约 4MB),保持默认值通常够用
  • 监控 -wal 文件大小,超过阈值就告警,这往往意味着有连接泄漏

另外,进程退出时如果还有连接没关闭,-shm 文件可能残留。它不是数据,删掉无害,但残留往往说明你的连接池没有正确回收,值得顺手查一下。

6. 四个踩过的坑

坑一:多进程写入没设 busy_timeout。表现是高峰期随机报 database is locked,日志里毫无规律。根因就是默认 timeout 为 0,锁一撞就失败。加上 5000 毫秒后彻底消失。

坑二:外键约束默认是关的。很多人以为写了 FOREIGN KEY 就生效,实际必须每个连接执行 PRAGMA foreign_keys=ON。漏了这一条,脏数据会在几个月后才暴露。

坑三:把库放在容器挂载的网络盘上。开发环境用 Docker volume 没问题,一旦换成 NFS 挂载,WAL 立刻报 disk I/O error。原因是共享内存映射跨主机不可用。SQLite 要求本地文件系统,这条没有折中方案。

坑四:用 ORM 的默认连接池。不少 ORM 默认开十几个连接,每个连接各自持锁排队,反而比单连接更慢。SQLite 场景下,写连接一个就够,读连接按 CPU 核数给,多余的连接只会增加锁竞争。

写在最后

SQLite 的价值不在性能数字,而在它把「数据库」这件事的运维成本压到了接近零:没有端口、没有账号、没有主从、没有版本升级窗口,备份就是一个文件。对一个只有几个人的项目来说,这份简单本身就是可靠性。

但它不是无条件的。写并发是硬上限,网络文件系统是硬禁区,备份方式必须换。把这三件事想清楚,剩下的配置照着上面抄就行。真正的风险从来不是选错数据库,而是用默认配置把它当成 MySQL 使。


你现在手上的项目,写操作大概是什么量级?也许它一直不需要那台云数据库。

0

凌晨四点的批发市场,一座城市最早醒来的地方

上一篇

AI 新闻日报 – 2026年10月27日:九百五十亿美元的融资,与六万人的买断

下一篇

评论 (0)

登录后可发布~
立即登录
暂无评论

admin

当生活给你的全是不如意,那你一定要相信自己的兴趣!
121
文章
0
评论
-11
喜欢
2026 年 10 月
一 二 三 四 五 六 日
 1234
567891011
12131415161718
19202122232425
262728293031  
« 9 月    

近期评论

聚合文章

截图工具四选一:ShareX、Snipaste、Flameshot、CleanShot X 的适用边界
啤酒瓶为什么大多是绿色的:一段从杂质开始的偶然
手机存储又满了:一份按收益排序的清理清单
K2-18b 的二甲硫醚:一颗海洋行星,和一条始终没被确认的信号
当记忆可以外包:一代人正在失去遗忘的能力
AI 新闻日报 – 2026年10月28日:会出声的世界模型,和一张要涨价的封装单
用 Prometheus 搭一套单机监控:四条告警规则,和三个必然踩到的坑
给父亲换了一部智能手机,我用了三个月才教会他三件事

猜你喜欢

Python 自动化办公:每天省2小时,从这些脚本开始

Python 自动化办公:每天省2小时,从这些脚本开始

admin
79 0 0
深入理解 JavaScript 异步编程:从回调到 async/await

深入理解 JavaScript 异步编程:从回调到 async/await

admin
91 0 0
Python自动化办公实战:用30行代码搞定重复工作

Python自动化办公实战:用30行代码搞定重复工作

admin
65 0 0
用 Caddy 2 做反向代理与自动 HTTPS:一份可复现的配置手册

用 Caddy 2 做反向代理与自动 HTTPS:一份可复现的配置手册

admin
19 0 0

关于我们

你是否对浩瀚的宇宙充满好奇?你是否渴望掌握未来科技的力量?现在,机会来了!“未来星球”将带你进入奇妙世界,开启你的科技探险之旅! 这是一个充满想象和创造力的博客!

分类导航

首页 新闻资讯 技术教程 软件工具 知识百科 生活实用 个人随笔 生命外星人

友情链接

未来星球AI工具【网络版】 未来星球 pycharm官方站【中国】
Copyright © 2026 未来星球. Designed by nicetheme. 蒙ICP备2026002569号-1
  • 请到 [后台->外观->菜单] 中设置菜单
  • AI未来
  • 生活百科
  • 档案
  • 外星人
  • 未来星球

admin

当生活给你的全是不如意,那你一定要相信自己的兴趣!
121
文章
0
评论
-11
喜欢