关于运维 SQLite,我学到的几件事

查看原文 HN 讨论

文章摘要

Julia Evans 分享了在一个 Django 网站上运行 SQLite 的经验。她的基调很诚实:SQLite 对小站点来说完全够用,但它「毕竟是一个数据库」,带着一整套她最初没充分意识到的运维复杂度。文章的价值不在于给出权威结论,而在于记录一个聪明的普通使用者遇到问题、动手排查、并坦诚说明哪些地方自己还没弄明白的完整过程。

最有传播力的发现是 ANALYZE 命令。她遇到一个全文检索查询,在一张只有 4000 行的表上竟然要跑 5 秒;跑了一次 ANALYZE 之后,执行时间降到 0.05 秒。ANALYZE 会生成统计信息,让查询计划器能做出更好的优化决策。她猜测最初的问题涉及某种意外的平方级复杂度行为,但坦言没有深究。

第二个发现是并发限制带来的真实故障。数据库清理操作出了问题:当 DELETE 语句超过 5 秒超时后,并发尝试写入的 worker 会崩溃,有时甚至导致虚拟机关闭。她采用的应对方式是「把这些清理操作拆成小批次做」,避免长时间持锁。她也承认这个限制正好说明了为什么有人会更愿意用 Postgres 这类支持多个并发写入者的「真正的」数据库。

运维实践方面,她描述了两套备份方案:一是用 Restic,先用 VACUUM INTO 做压实,再压缩上传到 S3;二是最近改用 Litestream 做增量备份,以减少备份进程被 OOM killer 杀掉的问题。她也提到备份监控用了「死人开关」(dead man’s switch)的思路。配置上,她一开始就按各种博客的推荐启用了 WAL 模式(预写日志),但承认没有深入研究其中细节。

其他零散观察包括:在她这个约一万行的小库上,Django ORM 查询还没到需要做性能监控的程度;当表之间不需要事务时,多个独立的 SQLite 文件可以替代单库架构;她的另一个项目「Mess with DNS」从 Postgres 迁到 SQLite 之后已经稳定运行了四年。

HN 评论精华