Django 小站的数据库只有 4000 行,一条全文搜索查询却跑到了 5 秒。这本该是眨眼的事——问题不在数据量,而在一条几乎没人会主动提起的命令:ANALYZE。
补上这条命令后,同一条查询降到约 0.05 秒,提速大约 100 倍。这是作者第 4 次把 SQLite 用作生产数据库,这次配的是 Django,这次踩坑也让他重新摸清了 SQLite 的运维边界。
这篇经历真正值钱的地方,不是 ANALYZE 本身,而是后面翻的车:批量清理数据时,SQLite 的单写者锁把整条写入路径堵死,某个 worker 超时崩溃,连带整台 VM 关机。SQLite 省的是运维一台数据库服务器,省不掉查询规划、写入排队和备份验证这些活。
查询为什么突然变慢:缺一条 ANALYZE
这条 FTS5 全文搜索查询,表里大约 4000 行,没跑 ANALYZE 之前耗时约 5 秒。补上之后降到约 0.05 秒,快了大约 100 倍。
ANALYZE 的作用,是把每张表大致有多少行、数据分布长什么样写成统计信息,交给查询计划器。没有这份统计,计划器等于蒙眼选路——哪怕表再小,也可能选中一条慢路径。作者没深究具体是哪一步执行计划走偏了,只确认了症状和药方对得上。
如果你的 SQLite 项目也在跑慢查询,有三件事比猜原因更快:
- 先跑一次
EXPLAIN QUERY PLAN,看有没有走上索引 - 手动执行
ANALYZE,或者在连接初始化时打开PRAGMA optimize,让 SQLite 在合适时机自己更新统计信息 - 数据量变化大的表,统计信息会过期,定期跑一遍比等查询变慢再排查划算
一次清理任务,为什么能让整台 VM 关机
比查询变慢更麻烦的是写入。作者要批量清理冗余记录(比如任务队列里堆积的已完成任务),流程翻车过好几次。
故障链条很清楚:清理事务跑超过 5 秒的写超时阈值,占住 SQLite 的写锁——SQLite 同一时间只允许一个写者。其他 worker 想写就得排队,排队超时就报错崩溃,worker 一崩,整台 VM 也跟着关机。
千里之堤,溃于蚁穴。一条本该几秒跑完的清理任务,只因为多攒了几千行数据,就顺着写锁一路捅到整机关机。worker 崩溃为什么会连带 VM 一起关闭,原文没有交代具体机制,这也是这个故事里最没底的一环——连崩溃怎么传导都说不清楚,运维手册就没法真正写完。
这背后是 WAL 模式常被误解的边界:它改善的是读和写之间的并行,不是让多个写者同时写。批量删除、大迁移这类长事务,照样会把写路径堵死。作者目前的对策是把清理拆成小批次,单批控制在几秒内;更稳妥的做法还包括设置合理的 busy_timeout 让写请求排队重试而不是直接报错,以及控制 WAL checkpoint 频率,避免 wal 文件无限增长拖慢后续读写。
这类项目该怎么选:分水岭不在数据量
作者这个项目数据库约一万行,预计长期都不会大——这确实是 SQLite 能扛住的规模。但决定 SQLite 用起来顺不顺的,从来不是数据量,而是并发写入的密度,以及团队愿不愿意花时间学它的运维细节。
| 维度 | 适合留在 SQLite | 该考虑迁移 Postgres |
|---|---|---|
| 数据规模 | 几千到几万行,长期增长慢 | 持续快速增长,难预判上限 |
| 写入并发 | 单进程或少数 worker,写入稀疏 | 多 worker/多服务同时写,写入密集 |
| 运维投入 | 愿意手动跑 ANALYZE、盯写锁 | 没精力盯底层细节,想要托管服务兜底 |
| 备份恢复 | 能接受自己验证恢复流程 | 需要成熟方案兜底,不想自己踩坑 |
备份这块作者也没完全解决。用 restic 做全量备份时,遇到过进程被 OOM 杀死、数据库锁住需要手动解锁;换成 Litestream 做增量复制后配置了保留窗口,但恢复流程有没有跑通,作者自己也没验证过。备份不等于灾备,没验证过恢复的备份,约等于没有备份。
另一个反例值得记一笔:作者的另一个项目把互不相关的表拆进多个 SQLite 文件,已经稳定跑了大约 4 年。这说明 SQLite 扛得住负载,只是要顺着它的脾气来:单文件、单写者、事务要短。写多读少、backup 没人管的团队,迟早要在深夜里重新学一遍写锁和备份这两件事。
过去两三年,SQLite 在独立开发者和小团队里明显更受欢迎,Litestream 这类增量复制工具也跟着火了起来——大家都想把"省一台数据库服务器"的诱惑变成现实。但这篇经历里最诚实的地方,恰恰是承认备份没验证过恢复。这才是"SQLite 复兴"叙事里最容易被略过的一环:工具变简单了,运维责任并没有跟着变少。
接下来值得盯的,是作者会不会真的做一次 Litestream 恢复演练,把保留策略也交代清楚——现在这两点都还是空白。
【锐评】简能省事,不能省责:SQLite 省下的是一台服务器,写锁与备份里的账,迟早要还。
