跑 SQLite 时我踩过的坑
Learning a few things about running SQLite
最近我在用 Django 搭建网站时选了 SQLite 做数据库。虽然大家都说小网站用 SQLite 没问题,但我还是发现了不少坑。比如没跑 ANALYZE 命令,查询慢到离谱;清理数据时因为锁表导致 Worker 崩溃;备份时甚至会被 OOM 杀掉。我也尝试了 restic 和 litestream 两种备份方案,还发现把表拆到多个数据库文件里也挺有用。虽然 SQLite 轻量,但真要跑起来,还是得懂点数据库运维的门道。
我一直以为 SQLite 很简单,直到今天才发现 ANALYZE 命令的存在,而我已经用 SQLite 做 Web 项目四年了!
HN 评论区
90- striking
> 也许有一天我会学会看查询计划。
借助 SQLite 的 `.expert` 模式,你可以把这一天稍微推迟一点:https://www.sqlite.org/cli.html#index_recommendations_sqlite...
sqlite> CREATE TABLE x1(a, b, c); -- 在数据库中创建表
sqlite> .expert
sqlite> SELECT * FROM x1 WHERE a=? AND b>?; -- 分析这条 SELECT 语句
CREATE INDEX x1_idx_000123a7 ON x1(a, b);
0|0|0|SEARCH TABLE x1 USING INDEX x1_idx_000123a7 (a=? AND b>?)
sqlite> CREATE INDEX x1ab ON x1(a, b); -- 创建推荐的索引
sqlite> .expert
sqlite> SELECT * FROM x1 WHERE a=? AND b>?; -- 重新分析同一条 SELECT 语句
(no new indexes)
0|0|0|SEARCH TABLE x1 USING INDEX x1ab (a=? AND b>?)
另外,关于
> 我目前的做法是将这些清理操作分成小批次执行,这样就不需要运行超过 5 秒的数据库查询。不过,这段经历让我更理解为什么有人想使用像 Postgres 这样的“真正”数据库,因为它能支持多个并发写入者。
针对那些“真正”数据库的建议通常也是将清理操作分批进行,只不过它们往往让这种小规模下的低效操作不那么明显。你比你想象的更正确!
- simonw
> 我一直备份到 AWS,但这总是很麻烦,因为要在 AWS 控制台中导航以生成凭证非常令人抓狂。
几年前我就被这搞烦了,以至于专门开发了一个工具来解决这个问题:
uvx s3-credentials create my-existing-s3-bucket
这会输出一组读写凭证,且权限严格限定在该桶(bucket)内。你可以添加 `--read-only` 或 `--write-only` 来进一步锁定权限,甚至添加 `--prefix foo/bar` 来限制凭证只能读写该桶内以该前缀开头的键。
> 也许有一天我会迁移到其他 S3 兼容的替代方案。
我用过 Restic 配合 Cloudflare R2,效果很棒。
- stevoski
作为一个搞数据库的人,这篇帖子我读得有点费劲。我本想找出问题所在并解决它们。
一个只有 1 万行的数据库表?即使是全表扫描也应该极快。
而且用的是 SQLite——我默认它是进程内运行的,但就算不是,它肯定也运行在同一台物理服务器上?那应该更快了。
当然,我脑子里蹦出的那句魔法咒语就是“创建索引”。
希望 Julia 能发布更新!
编辑:我高度怀疑这个“删除慢”的问题是许多 ORM 用户常遇到的经典“n+1”问题,直到他们更深入地理解底层的数据库交互机制。
- rollulus
我意识到在 LLM 时代,我更加欣赏 Julia 的写作了。这种真诚的探索是对那些过度自信、自以为是且由 AI 生成的垃圾文章的一剂解药。
- andrewaylett
我这样运行备份:
OUT="${i}.sql.zst"
PART="${OUT}.part"
sqlite3 -readonly "${i}" .dump | zstd --fast --rsyncable -v -o "${PART}" -
mv "${PART}" "${OUT}"
这不会阻塞写入者(前提是写入者使用了 WAL),生成的转储文件压缩率很高,同时也便于同步。我的 Home Assistant 数据库是 1.8GB,压缩后的转储文件是 286MB,我猜其中 90% 的内容每天都是重复的。