如何实时将 SQLite 备份到 S3 而不丢失数据
喜欢 SQLite 的简洁?启动一台新的 VPS,部署一个小服务,把数据库文件放在旁边,一切都能即时响应地运行。不需要额外的 PostgreSQL Docker 容器,不需要配置用户管理或访问权限。完美。
问题出现在服务器意外崩溃或提供商"意外"删除你的磁盘时。如果通过 cron 每天备份一次,你会丢失一天的用户和订单数据。如果在写入发生时用简单的 cp 复制数据库文件,最终会得到一个 SQLite 甚至无法打开的损坏文件。
知名键值数据库 BoltDB 的作者 Ben Johnson 面临了完全相同的困境,并构建了 Litestream。这是一个小型 Go 工具,一劳永逸地解决了 SQLite 的备份问题。
What Litestream Does and Why You Need It
Litestream 作为后台进程运行在与你的应用程序相同的服务器上。它的任务是持续将本地 SQLite 数据库的更改流式传输到远程存储。可以是 S3 兼容的云存储、Yandex Object Storage、MinIO,或者只是另一块挂载磁盘上的一个文件夹。
最终,你得到的是一个现成的灾难恢复系统。如果你的 VPS 彻底损毁,只需启动一个新实例,运行一条恢复命令,数据库就能恢复到崩溃前的最后一个事务。
该工具的主要特点是可靠性。它通过官方的 SQLite C-API 与数据库交互,而不是盲目地从磁盘读取原始字节。这保证了写入阶段不会与压缩或日志检查点重叠,数据库不会变成损坏的文件。
How Replication Works Under the Hood
要理解它的工作原理,让我们回顾一下 SQLite 的 WAL(预写日志)模式。
默认情况下,SQLite 直接将更改写入主 .db 文件。在 WAL 模式下,所有新事务首先进入带有 -wal 扩展名的单独日志文件。这些更改会定期刷新回主文件。这种方法可以加快写入速度,并且不会阻塞读取操作。
Litestream 利用这个机制:
- 它将数据库切换到 WAL 模式。
- 监控 WAL 文件中出现的新页面。
- 复制这些更改并以小段形式发送到 S3。
- 控制检查点过程,使 SQLite 不会在数据上传到云端之前过早清除 WAL。
发送到 S3 的数据延迟只有几秒钟。如果服务器损坏,最多只会丢失最后几秒钟的数据。
Setup and Usage Example
你可以在几分钟内启动复制。只需下载二进制文件或使用现成的 Docker 镜像。
首先,让我们创建配置文件 litestream.yml:
连接到 S3 的参数(如访问密钥和端点)通常通过标准环境变量传递:AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY。
该进程用一个简单的命令启动:
通常 Litestream 与主应用程序运行在同一个 Docker 容器中,使用轻量级进程编排器如 entrypoint.sh 或 supervisord。
如果服务器崩溃了,你需要将数据库部署到新位置,在启动应用程序之前运行恢复命令:
该工具会从存储桶中获取最新的基础转储,下载所有缺失的 WAL 段,组装一个完整的数据库文件,并将其放置到指定路径。
Limitations and Caveats
这个工具看起来非常吸引人,但 Litestream 有明确的范围和自身的限制。
首先,该工具要求启用 WAL 模式。如果你的应用程序或特定的数据库驱动库与它不兼容,那就无法工作。
其次,这是一个灾难恢复工具,而不是用于水平读取扩展的工具。你不能用 Litestream 在不同服务器上启动五个服务副本同时从同一个复制的数据库读取。对于有多个节点的分布式场景,你最好看看同一团队的 LiteFS 或 rqlite 这样的项目。
第三,如果你有一个数百 GB 的大型数据库且持续高强度写入,S3 流量和 API 请求费用可能会成为一个令人不快的惊喜。该工具是为中小型工作负载设计的。
Who Will Find This Project Useful
Litestream 消除了在不需要时设置重量级数据库的必要性。对于以下场景来说,这是一个绝佳的选择:
- Go、Python 或 Node.js 编写的 Telegram 机器人和 REST API,单个应用实例。
- 基于 SQLite 运行的 PocketBase 或 Directus 等框架项目。
- 在廉价 VPS 上运行的个人自托管服务。
- 具有本地缓存或客户端数据隔离的微服务。
如果你一直想把一个私人项目迁移到 SQLite,但担心磁盘丢失问题让你犹豫不决,试试添加 Litestream。设置需要半小时,但你就能高枕无忧了。
相关项目