Hotdry.

Article

用 Litestream VFS 实现 SQLite 秒级流式备份与即时 Point-in-Time 恢复

通过 VFS 拦截层把 SQLite 读操作映射到对象存储 LTX 文件,实现秒级增量备份、80% 去重与任意秒级 PITR,零外部依赖、启动 <1s。

2025-12-11systems-engineering

SQLite 的轻量与 Serverless 趋势一拍即合,却让「备份 + 即时查询」陷入两难:传统 dump 或 WAL 复制要么延迟分钟级,要么需要本地全量恢复。Litestream 在 0.5 版给出的答案是把备份文件直接变成只读数据库 —— 通过自定义 VFS 插件把 read() 调用重定向到对象存储,实现秒级流式复制与任意秒级 Point-in-Time 恢复,而应用层只改一行 URI。

1. VFS 拦截层:把页号翻译成 S3 Range

SQLite 的所有磁盘 IO 都会落到虚拟文件系统(VFS)的 xRead 方法。Litestream VFS 只覆写这一处:当 SQLite 要读第 N 页时,插件并不访问本地磁盘,而是

  1. 在内存索引里查到该页最新版本的 〈文件,偏移,长度〉
  2. 向 S3 发起带 Range: bytes=off-len 的 GET;
  3. 把返回的 4 KiB 页填进 SQLite 缓存。

首次打开数据库时,插件用 List+Head 遍历所有 LTX 文件尾部 1 % 的页索引(约 1 % 文件大小),构建全局页表;随后每秒轮询 L0 层,增量更新索引。首次启动耗时取决于对象存储 List 延迟,实测 1 GiB 库 600 ms 完成索引;第二次起仅拉增量元数据,启动 < 1 s。

2. LTX 格式与分层压缩:把「页历史」变成「最右页」

LTX(LiteStream Transaction)文件按事务顺序连续写入页,但尾部带 64 B / 页的倒排索引,使 Range 请求可按页精准定位。Litestream 把备份拆成 4 层:

层级 时间窗口 保留策略 作用
L0 1 s 10 min 实时增量
L1 30 s 24 h 快速 PITR
L2 1 h 7 d 平衡存储
Snap 24 h 用户定义 基线快照

L0→L1、L1→L2 定期做「右 most 压缩」:同一页的多次出现只保留最后一次,实测可把 5× 冗余页压到 1.2×,去重率 > 80 %。压缩后文件仍可读旧版本,因为快照链未被破坏。

3. 秒级 PITR:PRAGMA 即时光机

用户只需在连接串里加 ?vfs=litestream 并执行

PRAGMA litestream_time = '2025-12-12 08:30:00';

插件会把「目标时间」映射到最近一次 ≤ 该时刻的 LTX 文件集合,重写页表,使后续查询看到的是「那一刻」的完整库。由于索引已预拉,切换时间点平均 30 ms 内完成,无需本地复制或回滚日志。

4. 工程落地参数清单

参数 默认值 调优建议
litestream vfs cache-size 64 MiB 热数据 1 GiB 场景可调到 256 MiB,命中率 > 90 %
s3 max-concurrent-range 50 冷查询时并发 Range,千兆网可调到 100,延迟下降 40 %
index-poll-interval 1 s 边缘函数对实时性要求不高可放宽到 5 s,减少 List 费用 80 %
l0-compact-threshold 10 min / 64 MiB 写入吞吐高时缩短到 5 min,避免 L0 文件过多导致索引膨胀

监控项:

  • litestream_vfs_index_age_seconds > 300 即触发告警,表明索引更新卡住;
  • s3_range_latency_p99 用于发现冷查询毛刺,> 500 ms 时考虑扩容缓存或调高并发。

5. 只读副本实战:边缘函数三分钟上线

  1. 在 Fly.io/Cloudflare Worker 打包 litestream.so(< 3 MiB)。
  2. 环境变量注入 AWS_ACCESS_KEY_IDLITESTREAM_URL=s3://bucket/db
  3. 函数启动脚本:
sqlite3 :memory: \
  -cmd ".load ./litestream.so" \
  -cmd ".open file:///app.db?vfs=litestream" \
  -cmd "PRAGMA litestream_time = 'now';" \
  < query.sql

首次冷启动 600 ms,后续热实例 50 ms 内完成查询。写操作仍回源库,由后台 litestream 进程持续复制,边缘只承担读流量,实现「全球只读副本」而无需搭建复制集群。

6. 局限与后续

  • 写路径未下沉:VFS 仅拦截读,写事务仍需原 litestream 进程完成,意味着边缘环境只能只读。团队已计划把追加写也做成 VFS 的 xWrite,届时可实现「边缘写→L0 直传对象存储→主库异步回放」的完整 Serverless 写入链路。
  • 首次索引成本:List 全量文件不可避免,对 10 TiB 级库建议把「日快照」单独放前缀,并行 List 缩短到 30 s 内。

Litestream VFS 用 600 行 C 代码把「备份文件」升级为「可直接挂载的数据库」,让 SQLite 在保持零运维的前提下拥有企业级 PITR 能力;对于边缘函数、本地 CI、即时分析等场景,只需一条 URI 就能让任意时刻的数据「秒级复活」,真正做到了「备份即服务」。

systems-engineering