聊聊实操 SQLite 的几点心得

生成摘要
SQLite用于Django生产环境看似轻量,真正实操却绕不开查询优化、清理锁阻塞与备份可靠性:4000行FTS5查询因未执行ANALYZE耗时5秒,补上统计信息后降至大概0.05秒;大批量DELETE还可能让写入超时并导致工作进程崩溃。面对这些坑,分批清理、restic、Litestream及拆分数据库文件,哪些经验值得小型站点借鉴?
— AI 生成,仅供参考

最近我在开发一个 Django 网站,选用了 SQLite 作为数据库。刚开始把 SQLite 用于网站生产环境时,看过不少博客,都说小型站点用 SQLite 跑生产完全没问题。这点我也认同,但我之前没充分意识到:它终究是数据库,数据库本身很复杂,而我对数据库运维其实懂得不多。

下面分享我在实操 SQLite 时学到的几个小知识点。这已经是我第四个采用 SQLite 的网站项目了,但这次踩坑更多。得益于 Django ORM 的便利,相比不用 Django 的时期,我让数据库承担了更多工作量。

最开始我照着网上博客的教程,开启了 WAL 模式,之后就祈祷一切顺利。

 ANALYZE  居然这么关键

今天我对一张 4000 行的数据表执行查询,用的是 SQLite 的 FTS5 做全文检索,这条查询居然花了 5 秒。这明显不对劲,电脑明明跑的很快!

后来才发现,我需要执行  ANALYZE 。执行完之后,那条慢查询直接从 5 秒降到了大概 0.05 秒,速度快到我都懒得再深究细节。我至今还没彻底搞懂查询计划到底哪里出了问题,猜测大概率是无意间触发了平方级时间复杂度的低效逻辑。

 ANALYZE  会生成统计信息,大概包含每张表的行数,还有其他相关数据,帮助查询优化器做出更优的执行决策。

希望以后我能学会看懂查询计划。

数据库清理操作暗藏麻烦

有时候数据库里会多出一堆无用数据,举个例子,django‑tasks‑db 产生的已完成任务记录,我想要清理掉这些冗余行。

我已经好几次遇到下面这套连锁问题:

1. 执行清理数据的命令
2. 待删除的数据量大,命令执行耗时超过 5 秒。说实话我也疑惑为什么 DELETE 删除会这么慢,有可能是事务内部执行了大量 Python 代码,具体原因还不确定。
3. 清理进行过程中,其他业务工作进程尝试写入数据库。我设置的超时时间是 5 秒,于是写入直接超时。
4. 工作进程因为数据库写入失败直接崩溃,连带虚拟机也关机。

目前我的解决办法是分批执行清理,保证单条数据库操作耗时不会超过 5 秒。经历这件事之后,我更能理解为什么很多人会选择 PostgreSQL 这类真正支持多并发写入的数据库。

以后遇到这类维护,或许我会直接把网站下线做定时维护,不过这套流程我还没梳理出来。

ORM 查询性能:暂时还没太多体会

到目前为止,我直接用 Django ORM 写各种查询,基本不去关心查询性能。除了上面说到的  ANALYZE  的坑之外整体还算平稳。我的数据库体量不大,大概一万行,并且预估数据量不会暴涨,所以希望这套方式可以一直维持下去。

SQLite 的备份方案

我试过两种 SQLite 的备份方式。虽然我还没有实际演练过备份恢复流程,但会用守护告警(死 man 开关)监控备份任务是否正常运行。

方案一:restic

sqlite3 /data/calendar.db "VACUUM INTO '/tmp/calendar.sqlite'"
gzip /tmp/calendar.sqlite

# 将备份上传到 S3
# 有时备份进程因内存耗尽被系统终止,数据库会处于锁定状态,需要执行解锁
restic -r s3://s3.amazonaws.com/some_bucket/ unlock
# 执行备份,同时清理旧备份
restic -r s3://s3.amazonaws.com/some_bucket/ backup /tmp/calendar.sqlite.gz
restic -r s3://s3.amazonaws.com/some_bucket/ snapshots
restic -r s3://s3.amazonaws.com/some_bucket/ forget -l 1 -H 6 -d 2 -w 2 -m 2 -y 2
restic -r s3://s3.amazonaws.com/some_bucket/ prune

方案二:Litestream

最近我开始尝试 Litestream。restic 备份有时候会因为内存不足被系统杀掉,我有点受够这个问题,而增量备份看起来效率更高。只需要写一份配置文件,然后运行下面命令即可:

litestream replicate -config litestream.yml

我在配置文件设置了  retention: 400h ,希望可以保留数据库一段时间内的历史版本,不过我还不确定配置是否生效。

备份的存储后端选的是 AWS,体验很折腾,在 AWS 控制台里到处找位置生成密钥非常麻烦。以后或许我会换成其他兼容 S3 协议的对象存储。

可以拆分使用多数据库文件

我当前项目只用了一个数据库。但我做 Mess with DNS 项目的时候用到一个小技巧:把数据表拆分存到三个独立的 SQLite 文件,业务场景并不要求这些表必须放在同一个数据库,这么做效果很不错。

Mess with DNS 从 2022 年至今已经基于 SQLite 稳定跑了四年。从 PostgreSQL 迁移过来,对这个项目来说绝对是明智的选择。

结语

经常会有这种体验:折腾很久,才搞懂正在使用的技术里一些非常基础的知识点,还挺有意思。我 2022 年第一次在 Web 项目上用 SQLite,居然直到今天才知道还有  ANALYZE  这个命令。估计再过一两年,我又会解锁另一个基础特性。

© 版权声明
THE END
喜欢就支持一下吧
点赞13赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容