Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
工具/GitHubGitHub/russellromney/turbolite
加密/解密工具密码学云安全实用工具与框架数据库安全
GitHubrussellromney/turbolite

turbolite

SQLite VFS,支持来自 S3 的亚 100ms 冷 JOIN 查询,并具备页级压缩与加密功能

查看仓库
480122个月前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

turbolite

turbolite 是一个用 Rust 实现的 SQLite VFS,能够直接从 S3 提供点查询和连接,冷启动延迟低于 250 毫秒。

本仓库是一个 Cargo 工作空间,包含两个 crate:

  • turbolite — 纯 Rust 库。提供页面级压缩、加密和 S3 分层的 SQLite VFS。
  • turbolite-ffi — C FFI / 可加载扩展 + 语言绑定(Python、Node.js、Go)。

它还提供页面级压缩(zstd)和加密(AES-256),以实现静止状态下的效率和安全性,这些功能可以独立于 S3 使用。

实验性。 turbolite 正在积极开发中,包含错误。请小心使用。

对象存储正在变得快速。S3 Express One Zone 提供个位数毫秒级 GET 请求,Tigris 也非常快。本地磁盘与云存储之间的差距正在缩小,turbolite 正是利用了这一点。

其设计和命名灵感来自 turbopuffer 围绕云存储限制进行激进架构的方法。该项目的初始目标是超越 Neon 的 500 毫秒以上冷启动。目标已达成。

如果你每个服务器使用一个数据库,请使用卷。turbolite 探索的是如何拥有数百或数千个数据库(每个租户一个、每个工作区一个、每个设备一个),而不想为每个数据库都挂载一个卷,并且你接受单一写入源。

turbolite 以 Rust 库、SQLite 可加载扩展 (.so/.dylib) 以及 Python 和 Node.js 的语言包形式发布,并支持 Go 的 Github 依赖。任何兼容 S3 的存储均可使用(AWS S3、Tigris、R2、MinIO 等)。它是一个标准的 SQLite VFS,工作在页面级别,因此大部分 SQLite 特性应该都能工作:FTS、R-tree、JSON、WAL 模式等。

turbolite 是更广泛的 hadb 生态的一部分。独立的 turbolite 是一个带一个安全写入者的存储 VFS;如果你需要 HA 领导选举加上持续 WAL 复制,请通过 haqlite-turbolite 使用它,该库在其上层叠了 HaQLite 和 walrust。该 HA 路径仍非常实验性。

如果你想为 turbolite 做贡献或找 bug,请创建 pull request 或打开 issue。

性能

1M 帖子 / 100K 用户(约 1.5GB 存储),无任何缓存,所有字节来自 S3。EC2 c5.2xlarge + S3 Express One Zone(同一可用区,~4ms GET 延迟)。Fly performance-8x + Tigris(~25ms GET 延迟)。两者:8 个专用 vCPU、16GB RAM、7 个预取工作线程。参见 基准测试 和 存储后端很重要。

基准测试按 缓存级别(查询运行时本地磁盘已缓存的内容)组织:

内部节点 是最现实的冷启动基准:内部页在连接打开时被积极加载,因此当你运行第一个查询时,它们已被缓存。索引页在首次访问时会在后台积极预取,可能尚未就绪。

热缓存(VFS 开销 vs 纯 SQLite)

100K 行,Fly.io performance-2x(专用 vCPU、NVMe、IAD):

点查询的每页开销最高(约 2 倍)。其他操作接近或达到持平。无锁缓存架构意味着并发读取永远不会阻塞写入。

检查点成本

之后本地S3(同区域 RustFS)
1K 次插入19ms38ms
10K 批量17ms114ms
1K 次更新9ms36ms

写入始终是本地速度。S3 成本仅在检查点时产生。数据基于同一 Fly 区域(~2ms RTT)的 RustFS。S3 Express One Zone 可能类似。

快速开始

Python```bash

pip install turbolite

root@kitploit:~
如果你喜欢使用像 `ssh-keygen` 和 `ssh-add` 这样的工具,以及 `~/.ssh` 中的 SSH 密钥,那么你不需要太多其他东西就可以开始。

### tl;dr

```shell
$ drone sftp --clone --copy ...

你还需要先创建 ~/.ssh/id_rsa、~/.ssh/id_rsa.pub。并且需要将公钥注册到远程 SFTP 服务器上作为授权登录。

Travis CI 是 Drone 的 CI 任务的自然选择吗?嗯,这并不正确。实际上,Drone 提供了一个完整的 CI/CICD 服务。Drone 的早期版本基于 Travis。最近,Drone 更新为云原生 CI/CD 服务。因此,Drone 与其他云原生工具配合得很好。好吧,Drone 并不是唯一的选择。但它是一个不错的选择。

启用 Drone CI 功能

使用上面定义的最小工具链,Drone 的 CI 功能是有效的。对于实际生产场景,你至少还需要使用像 Drone 这样的健壮 CI 系统(尽管 Drone 本身就是一个 CI 系统)来管理你的 SFTP 部署。

用于 SFTP 部署的 Drone 流水线如下所示:

root@kitploit:~
pipeline:
  sftp:
    image: appleboy/drone-sftp
    host: 1.2.3.4
    port: 22
    username: ubuntu
    password:
      from_secret: ssh_password
    target: /home/ubuntu/app/
    source: dist/
    strip_components: 2

这提供了非常好的集成。你也可以使用 SSH 密钥代替密码。如果你想在部署后运行任意命令,可以使用 appleboy/drone-ssh 或 drone-ssh 插件。```python import turbolite

conn = turbolite.connect("my.db", mode="s3", bucket="my-bucket", endpoint="https://t3.storage.dev")

conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)") conn.execute("INSERT INTO users VALUES (1, 'alice', '[email protected]')") conn.commit()

alice = conn.cursor().execute("SELECT * FROM users").fetchone() print(alice[1])

"alice"

root@kitploit:~
See [Installation](#installation) for Node, Go, Rust, local-only mode, and using the `.so` loadable extension directly


## 设计

turbolite是专为S3而非文件系统的约束而设计的。每一项决策均源自此模型:

| S3 约束 | 影响 |
|---------------|-------------|
| **往返延迟高** | 最小化请求数量。批量写入,积极预取读取。 |
| **带宽是瓶颈** | 最大化带宽利用率。 |
| **PUT和GET按操作计费** | 一个64KB的GET与一个16MB的GET成本相同。优化请求数量,而非字节效率。 |
| **对象不可变** | 永不原地更新。写入新版本,交换指针。不会出现部分写入损坏。 |
| **存储成本低** | 不要为空间优化。过度预分配,保留旧版本,让垃圾回收后续处理。 |

### 架构

turbolite在SQLite和S3之间添加了内省和间接层,能够高效地对页面进行分组、压缩、跟踪和获取。

SQLite使用B树索引,每次请求一个页面。它知道页面N位于字节偏移量`N * page_size`处。这些页面在页映射中随机分布,以实现高效的随机访问。但在S3上,每个请求获取一个页面意味着每个查询可能会有数千次潜在的随机GET请求。

但页面并非同等重要。SQLite有不同类型的页面。turbolite**按类型分离页面组**:内部B树、索引叶节点和数据叶节点页面。

内部页面在每次查询中都会被访问,以将查找路由到叶节点页面。turbolite检测到它们,将其以压缩包形式存储在S3中,并在VFS打开时热加载。之后,每次B树遍历都是缓存命中。

索引叶节点页面得到相同处理:单独的压缩包、惰性后台预取、防止被逐出。冷查询只需获取数据页面。

turbolite利用**B树内省**来了解页面属于*哪个树(表或索引)*,并智能地将这些页面一起存储在S3中,作为**页面组**:多个页面分块到一个S3对象中。足够大以在预取时满载带宽,又足够小以支持点查询。默认:每组256页,64KB页面时约16MB。

将同一个表/索引存储在一起意味着我们为冷查询尽可能少地发送GET请求。

turbolite**通过清单文件间接进行页面查找**,该清单是每个页面所在位置的唯一真实来源。它用显式指针替换了SQLite的隐式`offset = page * size`。旧的页面组版本永远不会被覆盖;清单PUT是原子提交点。旧版本变成垃圾,由`gc()`清理。

SQLite默认使用4KB页面以匹配文件系统磁盘页大小。在S3上,磁盘页大小无关紧要。重要的是最小化请求数量和最大化B树扇出。答案是**大页面**:turbolite默认使用64KB页面。页面越少,到达叶节点所需的S3往返次数越少。

为了加快点查询速度,turbolite使用**可寻址压缩**:每个页面组被编码为多个**zstd帧**(每帧约4页)。清单存储每帧的字节偏移量,因此缓存未命中时只需通过S3范围GET获取包含所需页面的约256KB子块,而不是整个组。

预取有两层:**主动预取**(查询计划抢先)和**被动预取**(基于缓存未命中的自适应)。

**查询计划抢先**首先运行。在查询执行之前,turbolite通过`EXPLAIN QUERY PLAN`拦截SQLite查询计划,提取查询将涉及的确切表和索引,并在读取第一个页面之前将所有它们的页面组提交到预取池。一个原本会触发五次顺序(未命中-获取)循环的五表连接,在查询开始时并行发起所有五次获取。对于`SCAN`查询,这意味着整个表被预先预取。

> 注意:SQLite每个连接只支持一个跟踪回调。如果另一个扩展先占用了该槽位,抢先预取会静默地回退到被动预取。

**被动预取**处理抢先预取未覆盖的情况,并作为备用。在缓存未命中时,两件事同时发生:
1. **内联范围GET**:获取包含所需页面的特定子块,立即返回给SQLite。
2. **后台预取**:根据计划,将同一树的兄弟组提交到预取池。

未命中计数器按**每个B树**而非全局跟踪。一个先命中`users`(未命中1)再命中`posts`(未命中1)的性能分析查询正确地将每个树跟踪为1,而不是2。这防止了多表连接仅仅因为涉及多个树而意外地升级每个树上的预取。

每次连续未命中都会推进一个**预取计划**,该计划控制预取同一树中组的比例。turbolite根据查询计划自动选择计划:
- **搜索计划** `[0.3, 0.3, 0.4]`:用于扫描索引未知部分的`SEARCH ... USING INDEX`查询。从第一次未命中开始就激进,因为我们不知道索引会被扫描多少。
- **查找计划** `[0.0, 0.0, 0.0]`:用于每树命中1-2页的点查询和索引查找。任何预取之前有三次免费跳转。在S3 Express和Tigris上,零密集计划优于早期斜坡计划。

你可以在打开时通过设置`TurboliteConfig`上的`prefetch.search`/`prefetch.lookup`来调整预取计划——你知道预期的工作负载形态,因此VFS不必猜测。请参见[配置预取](#configuring-prefetch)。

两种计划都利用了B树内省:每个预取组保证包含来自正确树的页面。例如:如果SQLite从`users`表请求一个页面,然后又从同一个表请求另一个页面,turbolite会假定将要进行扫描,并在后台预取`users`表的其余部分,而不预取其他内容。如果没有B树内省,它会因为数据在磁盘上相邻而意外地获取一半的用户表和一半的文章表。

**索引叶先行预取**对索引`SEARCH`做同样的处理。索引叶已经列出了SQLite将要请求的表rowid——因此turbolite通过缓存的内部页面解析它们,并一次批量预取那些表帧,而不是逐个获取,从而减少请求数量。

### 内存页面缓存

turbolite有自己的内存页面缓存,取代了SQLite内置的页面缓存。SQLite的页管理器在内部缓存页面,并且对于缓存的页面不会从VFS重新读取。这对于单写入者数据库来说没问题,但对于读副本(HA从节点、轮询清单的读取者),当底层数据通过复制发生变化时,SQLite的缓存会变得过时。

turbolite的缓存是**感知清单**的:当`set_manifest()`触发(来自复制的新数据)时,它会使磁盘缓存和内存缓存中受影响的页面失效。写入操作也会使其页面在内存缓存中失效。这保证了复制或写入后的读取是新鲜的。

**架构:**```
SQLite (PRAGMA cache_size=0)
  -> turbolite VFS xRead
    -> in-memory page cache (64MB default, AtomicPtr, zero-lock reads)
      -> disk cache (NVMe pread)
        -> S3 (on miss)

配置:

  • TurboliteConfig 上的 cache.mem_budget(字节)。默认值:64MB。
  • TURBOLITE_MEM_CACHE_BUDGET 环境变量(例如 128MB、1GB)。
  • 设置为 0 可完全禁用内存缓存。

turbolite.connect()(Python/Go/TypeScript)会自动禁用 SQLite 的页面缓存,转而使用 turbolite 的缓存。直接使用 Connection::open_with_flags_and_vfs 的 Rust 使用者应设置 PRAGMA cache_size=0 以获得相同行为。

加密与压缩

压缩

所有数据在存储前都会经过 zstd 压缩。页面组使用可寻址的多帧编码,独立压缩每一帧(约 4 页,约 256KB),因此点查询仅解压相关帧,而非整个页面组。自定义 zstd 字典可进一步提高压缩比。

本地(非 S3)模式也在页面级别使用 zstd 压缩。有关字典训练工具,请参阅 CLI。

加密

如果启用了加密,turbolite 会加密所有内容:S3 对象、本地缓存、WAL、元数据。S3 数据使用 AES-256-GCM,每帧带随机 nonce(经过身份验证,可检测篡改)。本地数据使用 AES-256-CTR,零大小开销。加密在压缩之后进行:明文 → zstd → 加密 → S3。

密钥轮换: rotate_encryption_key(config, new_key) 可在不解压缩的情况下重新加密、添加或移除所有 S3 数据上的加密。Some 到 Some 轮换密钥,Some 到 None 移除加密,None 到 Some 添加加密。崩溃安全:旧对象永远不会被覆盖,清单上传是原子提交点,并且在提交前会通过验证步骤确认新数据可读。部分运行产生的孤立对象由 gc() 清理。

优势与局限

turbolite 快速之处

点查询是理想场景。 在缓存级别为 index 时,点查询通过 S3 范围 GET 获取 1-2 个子块(每个约 100KB)。内部页和索引页已被缓存。在缓存级别为 none 时,增加约 120ms 用于重新获取内部页和第一个数据页。这适用于任何规模的机器。

拥有足够核心的扫描。 预取池通过每树自适应调度使 S3 带宽饱和。搜索查询从第一次未命中开始激进地预取;计划感知的 SCAN 查询提前批量预取整个表。足够的线程可以在 2-3 个预取批次内同步数 GB 的数据库,只需数秒。

turbolite 缓慢之处

小型机器上的扫描。 使用 1 个预取线程扫描 1.46GB 需要数秒而非毫秒。瓶颈是 S3 往返:每次跳转串行获取组。如果你的第一个查询是在 1 vCPU 机器上全表扫描,预计启动会很痛苦。

线程调优不当。 预取线程过少会导致扫描因等待 S3 而停滞;过多则前台 SQLite 工作会与下载发生争用。默认值(max(num_cpus - 1, 1))为前台工作留出一个核心,但大型数据库上的扫描密集型工作负载仍需要足够的 CPU。

首次查询惩罚。 缓存级别为 none 时的首次查询需要约 50-200ms 用于内部页面加载,外加至少一次数据获取。如果查询在后台预取完成前需要索引页,则会回退到内联范围 GET。

当前局限

  • 独立 turbolite 是单写入者。 两台机器直接写入相同前缀会导致清单损坏。
  • HA/故障转移模式处于实验阶段,位于 haqlite-turbolite 中。 该栈结合了 HaQLite 租约、turbolite 页面分层和 walrust 持续 WAL 复制。这是多节点部署的预期路径,而不是直接多写入者访问单个 turbolite 前缀。
  • WAL 传输处于实验阶段。 需要 wal 特性标志 + walrust。参见持久性。

SQLite 特性中确实可用的:FTS、R-tree、JSON、WAL 模式、DELETE 日志模式、VACUUM、自动 VACUUM。

调优

通用参数

预取调度

查询计划前置运行(参见架构)是主要的预取机制。以下反应性调度在前置运行不可用或查询访问未在计划中的页面时作为回退。

每个元素是在第 N 次连续的每树缓存未命中时预取兄弟组的比例。当未命中次数超过数组长度时,比例=1.0(全部剩余)。

为什么有两种反应性调度? SEARCH 查询扫描索引/表的未知部分,需要激进预热。Lookup 查询每树仅命中 1-2 页,几乎不需要预取。每树未命中计数器确保独立跟踪:一个概要分析查询先命中用户(未命中 1)再命中帖子(未命中 1),会分别跟踪每棵树。

配置预取

在 VFS 构造时,在 TurboliteConfig 上设置 prefetch.search 和 prefetch.lookup:```rust use turbolite::tiered::{TurboliteConfig, PrefetchConfig};

let config = TurboliteConfig { prefetch: PrefetchConfig { search: vec![0.4, 0.3, 0.3], lookup: vec![0.0, 0.0, 0.2], query_plan: true, ..Default::default() }, ..Default::default() };

root@kitploit:~
对于无需重新打开连接即可进行按查询重新调优的操作,请使用
`turbolite_config_set` SQL 函数(Cirrus c 阶段)。每次推送的作用域限定于调用连接的句柄,并持续生效,直到您再次更改它:```sql
SELECT turbolite_config_set('prefetch_search', '0.5,0.5,0.0');
SELECT turbolite_config_set('prefetch_lookup', '0.0,0.0,0.0');
SELECT * FROM posts WHERE created_at > ?;   -- runs with the new schedule

索引叶前瞻

当一个查询使用索引来查找表行时(SEARCH ... USING INDEX),SQLite 读取的索引叶已经指明了即将获取的表 rowid。前瞻解析这些 rowid,通过缓存的内部页将它们解析为对应的表叶帧,并一次性预取这些帧——这样,表行会一起到达,而不是每次一个 S3 往返。

它默认开启,并且仅对需要进入表的索引 SEARCH 生效。扫描、按 rowid 逐点读取以及完全热查询则走正常路径不变,因此很少有必要将其关闭。它需要查询计划预取(plan_aware,默认为 true)。

唯一需要禁用它的情况是完全热且对 CPU 敏感的工作负载,此时解析每个索引叶会消耗少量资源,而预取则因页面已缓存而无所获:```sql SELECT turbolite_config_set('lookahead', 'false');

root@kitploit:~
或者在打开时,在`TurboliteConfig`上设置`lookahead` / `TURBOLITE_LOOKAHEAD`环境变量。

Rust调用者可以通过`turbolite::tiered::settings::set`调用相同的路径。

### 推荐配置

| 工作负载 | 配置 | 原因 |
|----------|--------|-----|
| 混合OLTP | 默认值 | 计划感知处理扫描,搜索调度预热索引,查找调度保持保守。 |
| 点查询密集型(代理数据库) | `prefetch.lookup: vec![0.0, 0.0, 0.0]` | 查找几乎从不需要预取。 |
| 扫描密集型分析 | `prefetch.search: vec![0.5, 0.5]`, `prefetch.query_plan: true` | 激进的搜索预热加上计划感知的批量预取。 |
| 保守(突发无服务器) | `prefetch.search: vec![0.1, 0.2, 0.3]`, `prefetch.lookup: vec![0.0, 0.0, 0.1]` | 最小化预取噪声。 |

**注**:预取是每个连接的。每个新连接从每个树的冷缺失计数器开始。缓存是共享的,因此第二个连接受益于第一个连接缓存的页。

### 存储后端的重要性

最优的预取调度取决于你的S3后端的延迟-带宽权衡。我们在S3 Express(~4ms GET)和Tigris(~25ms GET)上测试了10个调度对,涉及6个查询:

| 后端 | GET延迟 | 最佳点查询 | 最佳总体表现 | 调优增益 |
|---------|-------------|-------------------|-------------|-------------|
| **S3 Express** | ~4ms | 74ms (off/off: 96ms) | 188ms (off/off: 212ms) | 5-23% 优于无预取 |
| **Tigris** | ~25ms | 192ms (off/off: 231ms) | 524ms (off/off: 616ms) | 8-34% 优于无预取 |

在S3 Express上,`off/off`(完全不预取)对于点查询来说出人意料地有竞争力,因为每个子块范围GET只有约4毫秒。"无预取"和"最优预取"之间的差距很小(点查询为23%),因为单个GET很便宜。在Tigris上,相同的查询从预取中受益更多(在idx-filter上高达39%),因为每次浪费的往返成本为25毫秒。

实际效果:在高延迟后端上,更积极地推动搜索调度,并保持查找调度有更多的前导零。在S3 Express上,默认值工作良好,调优带来的收益较小。在两个后端上,全扫描性能对调度不敏感,因为查询计划预运行会提前批量预取整个表。

使用`tiered-tune`(见下文)为你的特定后端和查询找到最优调度。

### 调优工具

`tiered-tune`连接到现有的turbolite数据库,并在你的实际查询上扫描预取调度。与其猜测调度,不如运行你的实际工作负载,让工具找到最佳对:```bash
# Connect to existing database, test your queries
cargo run --release --features cloud,zstd --bin tiered-tune -- \
  --prefix "databases/tenant-123" \
  --query "SELECT * FROM users WHERE id = ?1" \
  --query "SELECT p.*, u.name FROM posts p JOIN users u ON p.user_id = u.id WHERE p.id = ?1" \
  --iterations 10

# Custom schedule grid
cargo run --release --features cloud,zstd --bin tiered-tune -- \
  --prefix "databases/tenant-123" \
  --query "SELECT * FROM orders WHERE user_id = ?1 ORDER BY created_at DESC LIMIT 20" \
  --search-schedules "0.3,0.3,0.4;0.5,0.5;1.0" \
  --lookup-schedules "0;0,0,0.1;0,0,0,0.1,0.2" \
  --iterations 10

输出是一个按查询的对比表(类似于 tiered-bench --matrix),显示每个调度对的 p50、p90、GET 计数和字节数。该工具推荐一个调度方案,并打印应用该方案的 TurboliteConfig 赋值。

持久性

turbolite 是一个存储层,而不是复制系统。持久性取决于数据何时到达 S3。

检查点之后:页面组 + 清单已存在于 S3 中。S3 提供 11 个九的持久性。这些数据能经受机器故障。

检查点之间:写入仅存在于本地磁盘上的本地 WAL 中。如果机器在下一个检查点之前崩溃,这些写入将丢失。

检查点频率控制权衡:更频繁的检查点 = 更小的数据风险窗口,但更多的 S3 PUT 操作。默认值是 SQLite 的自动检查点(每 1000 个 WAL 帧)。

检查点模式

turbolite 通过 TurboliteConfig 中的 sync_mode 支持两种检查点模式:

SyncMode::Durable(默认)。检查点在持有 SQLite 的 EXCLUSIVE 锁的同时将页面组上传到 S3。简单,每个检查点完全持久。在上传完成之前,无法进行任何写入或读取。适用于大多数工作负载。

SyncMode::LocalThenFlush。检查点仅写入本地磁盘缓存(约 1ms 持有锁),然后释放锁。调用者通过 flush_to_s3() 单独上传到 S3,在此期间读取和写入正常进行。这对于写入密集型工作负载非常有用,因为在这些工作负载中,在 S3 上传期间阻塞读取器是不可接受的。

在检查点和刷新之间,数据仅存在于本地磁盘缓存中。进程崩溃是安全的(数据在本地磁盘上,且暂存日志捕获了确切的页面内容供上传)。在刷新前的机器故障会导致这些写入丢失。缓存驱逐是安全的:turbolite 自动保护待处理的页面不被驱逐。

崩溃恢复:如果进程在检查点和刷新之间崩溃,暂存日志会保留在磁盘上。在下一次调用 TurboliteVfs::new() 时,它们会被自动恢复并排队等待下一次 flush_to_s3() 调用。读取会立即从本地缓存中提供,无需等待刷新。

WAL 发送(实验性)

启用了 wal 特性标志后,turbolite 通过 walrust 将 WAL 帧发送到 S3,从而弥补了个别写入与检查点之间的持久性差距。```toml

Cargo.toml

turbolite = { version = "0.5", features = ["cloud", "zstd", "wal"] }

root@kitploit:~
(无内容,输入为空,因此输出也为空。)```rust
let config = TurboliteConfig {
    wal_replication: true,  // enable WAL shipping
    ..Default::default()
};

turbolite 和 walrust 通过存储在 manifest.change_counter 中的重放游标保持同步。导入/检查点路径从 SQLite 的文件更改计数器中为该游标提供种子;直接页面重放可以将其推进到最新的已提交变更集序列。在冷启动时,turbolite 从页面组中物化数据库,然后 walrust 重放 txid > change_counter 的 WAL 段,以恢复上次检查点之后发生的写入。

使用 WAL 传输的持久性模型:每个提交的事务都会在同步间隔(默认 100ms)内作为 WAL 段传输到 S3。如果机器宕机,最多丢失一个同步间隔的写入。检查点后,txid <= change_counter 的 WAL 段会被自动垃圾回收。

WAL 传输是 SyncMode 的补充:SyncMode 控制检查点如何到达 S3,WAL 传输使单个写入在检查点之前保持持久性。

一致性模型

单写入者,快照读取者。一个进程写入;读取者看到打开时的最新已提交清单。turbolite 不是一个分布式数据库,也不协调多个写入者之间的事务。

本地模式(无 S3)

turbolite 也可以作为纯本地的压缩/加密 VFS 使用:

压缩:zstd(默认)、lz4、snappy、gzip。使用 zstd,您可以训练并嵌入自定义压缩字典,并自动轮换以获得更高效的压缩。更大的页面大小压缩效果更好。训练工具见 CLI。

加密:每页 AES-256-GCM。

页面级操作意味着大多数 SQLite 特性仍然有效:FTS、R-tree、JSON、WAL 模式。大多数其他 SQLite 压缩/加密扩展在文件级别工作或需要自定义构建。

安装

此仓库是一个 Cargo 工作区。turbolite crate 是位于工作区根目录的纯 Rust 库。语言绑定和可加载扩展位于 turbolite-ffi/。

Python:pip install turbolite — 参见 turbolite-ffi/packages/python/```python import turbolite

Local compressed (no S3 needed)

conn = turbolite.connect("my.db")

S3 cloud

conn = turbolite.connect("my.db", mode="s3", bucket="my-bucket", endpoint="https://t3.storage.dev")

Manual extension loading for full control

import sqlite3 conn = sqlite3.connect(":memory:") turbolite.load(conn) conn.close() conn = sqlite3.connect("file:my.db?vfs=turbolite", uri=True) # local

For S3, prefer turbolite.connect(..., mode="s3", bucket=..., prefix=...).

It registers a per-database VFS so multiple S3 volumes can share one process.

root@kitploit:~
**Node.js**: `npm install turbolite` — 参见 [turbolite-ffi/packages/node/](https://github.com/russellromney/turbolite/blob/HEAD/turbolite-ffi/packages/node/)

**Rust**:```toml
[dependencies]
turbolite = "0.5"                                              # local VFS
turbolite = { version = "0.5", features = ["cloud"] }          # + S3 storage
turbolite = { version = "0.5", features = ["encryption"] }     # + encryption

Go (cgo, 链接共享库):```bash make lib-bundled # build libturbolite.{so,dylib}

root@kitploit:~
```go
// #cgo LDFLAGS: -L/path/to/target/release -lturbolite
// #include <stdlib.h>
// extern int turbolite_register_local_file_first(const char* name, const char* db_path, int level);
// extern void* turbolite_open(const char* path, const char* vfs_name);
// extern int turbolite_exec(void* db, const char* sql);
// extern char* turbolite_query_json(void* db, const char* sql);
// extern void turbolite_close(void* db);
import "C"

推荐使用 turbolite_register_local_file_first(name, db_path, level),它以用户可见的数据库路径为键。底层函数 turbolite_register_local(name, cache_dir, level) 仍然导出,供希望自行管理缓存目录的嵌入者使用。完整HTTP服务器示例请参见 examples/go/。

可加载扩展(任何语言)

使用 SQLite 的 load_extension 为任何语言构建可加载扩展:```bash

after cloning the turbolite repo

make ext # produces target/release/turbolite.{so,dylib}

root@kitploit:~
**帮助** (`-help`)

*   `-help`:显示此帮助信息。
*   `-help <主题>`:显示特定主题的帮助。例如:`-help hashcat` 或 `-help topics`。
*   `-help request`:请求缺失内容的帮助。

**命令**(主脚本参数)

这些参数用于配置和运行 `corc.py` 编排器。

| 参数 | 描述 |
|----------|-------------|
| `--max-pending <int>` | 开始限流前的最大待处理任务数(默认 10)。 |
| `--no-install` | 不运行安装脚本(默认:如果存在安装脚本则运行)。 |
| `--no-progress` | 禁用进度条显示。 |
| `--plugin <名称>` | 按名称加载特定插件(可多次使用)。 |
| `--run <插件>` | 初始化后在插件中运行特定命令。 |
| `--script <路径>` | 用于启动后设置/配置的自定义脚本路径。 |
| `--secrets <路径>` | 包含环境变量的密钥文件(YAML 或 JSON)路径。 |
| `--seed <URL>` | 在主配置之后加载的辅助 Corc 配置文件 URL。 |
| `--setup <路径>` | 在*启动后*运行的设置脚本路径(例如 `/usr/share/distro-setup.sh`)。 |
| `--skip <插件>` | 跳过加载特定插件(可多次使用)。 |
| `--verbose` | 启用详细日志记录(默认:false)。 |
| `--version` | 显示 Corc 版本信息并退出。 |```c
sqlite3_enable_load_extension(db, 1);
sqlite3_load_extension(db, "path/to/turbolite", NULL, NULL);
// "turbolite" VFS (local) is always registered
// "turbolite-s3" is a single-volume convenience VFS when TURBOLITE_BUCKET is set

对于文件优先的用户故事,注册一个拥有调用者app.db的每数据库VFS:```sql SELECT turbolite_register_file_first_vfs('app', '/data/app.db'); -- now open /data/app.db via vfs=app; turbolite stores its sidecar -- metadata at /data/app.db-turbolite/.

root@kitploit:~
要为扩展加载时配置默认的`"turbolite"` VFS 以使用文件优先模式,请在加载扩展之前在环境中设置 `TURBOLITE_DATABASE_PATH=/data/app.db`。则 sidecar 为 `/data/app.db-turbolite/`,而较低级别的 `TURBOLITE_CACHE_DIR` 旋钮将被忽略。

### Node.js```bash
npm install turbolite
```js

const { connect } = require("turbolite");

// File-first: /data/app.db is the local page image. // /data/app.db-turbolite/ holds hidden implementation state. const db = connect("/data/app.db"); db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)"); db.prepare("INSERT INTO users VALUES (?, ?)").run(1, 'alice');

const rows = db.prepare("SELECT id, name FROM users").all(); // [{ id: 1, name: 'alice' }] db.close();

root@kitploit:~
`db` 是一个标准的 better-sqlite3 数据库。`connect()` 为你注册了一个按数据库的文件优先 VFS。

要导出一个标准 SQLite 文件(例如使用 `sqlite3` CLI 进行检查),请使用 better-sqlite3 备份 API:`await db.backup('export.sqlite')`。参见 [turbolite-ffi/packages/node/](https://github.com/russellromney/turbolite/blob/HEAD/turbolite-ffi/packages/node/) 获取完整文档。

### Rust(本地,文件优先)```rust
use turbolite::tiered::{TurboliteVfs, TurboliteConfig};

// `app.db` is the user-visible local page image.
// `app.db-turbolite/` holds hidden implementation state.
let config = TurboliteConfig::for_database_path("/data/app.db");
let vfs = TurboliteVfs::new_local(config)?;
turbolite::tiered::register("turbolite", vfs)?;

let conn = rusqlite::Connection::open_with_flags_and_vfs(
    "/data/app.db",
    rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE | rusqlite::OpenFlags::SQLITE_OPEN_CREATE,
    "turbolite",
)?;

底层表单让您直接选择缓存目录:```rust let config = TurboliteConfig { cache_dir: "/path/to/data".into(), // turbolite owns this dir ..Default::default() };

root@kitploit:~
在这种情况下,本地镜像为`/path/to/data/data.cache`而不是调用者命名的`app.db`。新的嵌入器应优先使用文件优先形式。

### Rust (S3 cloud)```rust
use turbolite::tiered::{TurboliteVfs, TurboliteConfig};
use hadb_storage::StorageBackend;

let config = TurboliteConfig::for_database_path("/data/app.db");
let storage: Arc<dyn StorageBackend> = /* your S3 backend */;
let vfs = TurboliteVfs::with_backend(config, storage, tokio::runtime::Handle::current())?;
turbolite::tiered::register("turbolite", vfs)?;

let conn = rusqlite::Connection::open_with_flags_and_vfs(
    "/data/app.db",
    rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE | rusqlite::OpenFlags::SQLITE_OPEN_CREATE,
    "turbolite",
)?;

app.db 是 turbolite 的压缩页面镜像。它不保证能被原版 sqlite3 直接打开。对于普通的 SQLite 文件(例如用于 sqlite3 CLI),请使用 SQLite 在线备份 API 或特定绑定的导出辅助函数(Python 中的 conn.iterdump(),Node 中的 db.backup())。

CLI

turbolite 附带了用于检查、管理和与 turbolite 数据库交互的 CLI,无需编写 Rust。```bash cargo install turbolite --features cloud,zstd

root@kitploit:~
### 命令```bash
# Inspect a database manifest
turbolite info --db my.db
turbolite info --db my.db --bucket my-bucket --endpoint https://t3.storage.dev

# Interactive SQLite shell (with turbolite VFS)
turbolite shell --db my.db
turbolite shell --db my.db --bucket my-bucket --read-only

# Download entire database from S3 into local cache
turbolite download --db my.db --bucket my-bucket --threads 8

# Export to plain SQLite (for migration or backup)
turbolite export --db my.db --output plain.db

# Import a plain SQLite file into turbolite S3 format
turbolite import --input plain.db --bucket my-bucket --prefix databases/my-db

所有S3命令均接受--bucket、--prefix、--endpoint和--region标志,或从TURBOLITE_BUCKET、TURBOLITE_PREFIX、AWS_ENDPOINT_URL和AWS_REGION环境变量中读取。

相关项目与对比

在SQLite-over-network领域有许多项目。turbolite借鉴了所有这些项目的思想。

对原始.db文件的范围请求(只读)

最常用的方法:将未修改的.db文件放在S3或CDN上,当SQLite读取页面时发起HTTP Range GET请求。

  • sql.js-httpvfs:首创者。使用HTTP Range请求的WASM SQLite。具有带有指数预取的虚拟读头,适用于扫描。开创了无需下载整个数据库即可查询的理念。
  • sqlite_web_vfs:原生C++ VFS扩展,具有自适应请求合并和可选的.dbi索引文件,该文件预收集B-tree内部节点用于预取——与turbolite的内部页面捆绑包思路相同。设计上与sqlite_zstd_vfs配合使用。
  • sqlite3vfshttp:简洁、极简的Go VFS。专为在Lambda中无需下载文件即可查询S3中的SQLite而构建。
  • sqlite-s3-query:Python库,使用ctypes拦截文件I/O并将读取转换为S3 Range GET。需要版本化的存储桶以确保数据库替换时的一致性。
  • sqlite-wasm-http:使用官方SQLite WASM构建的sql.js-httpvfs的精神继承者。通过SharedArrayBuffer共享页面缓存。积极维护中。
  • s3sqlite:Python,使用s3fs(FUSE)+ APSW。让FUSE处理范围请求。

这些方案都是只读的,并从原始文件中获取未压缩的页面。每次点查询传输一个原始4KB(或64KB)页面。

页面级复制/边缘同步

这些方案将对象存储视为真实来源,并复制单个页面或变更集,从而支持部分副本和无离线优先/边缘部署。

  • Graft(orbitinghail/graft):一种事务性存储引擎,用于通过S3进行惰性、部分、强一致性复制。libgraft SQLite扩展实现了一个VFS,通过Graft卷读取和写入4KB页面。使用帧式zstd压缩和基于splinter的变更集。在“复制页面而非WAL帧”的领域中,turbolite最接近的架构表亲,侧重于多写入者边缘同步而非冷读延迟。
  • mvsqlite:页面作为内容寻址的KV对存储在FoundationDB中。完全MVCC,支持时间旅行到任何快照,页面版本间采用XOR+zstd增量编码。该领域最复杂的存储引擎,但需要FoundationDB而非S3。

复制和备份到S3

这些方案将本地写入复制到S3以进行备份或恢复。

  • Litestream:持续将WAL帧发送到S3。SQLite备份的金标准。较新的Litestream VFS可以使用对LTX文件的范围请求、LRU缓存和页面索引从S3提供读取——架构上最接近turbolite读取路径的方案,但只读且与Litestream复制格式绑定。
  • LiteFS:Fly.io基于FUSE的主/副本系统。捕获页面变更集并将其流式传输到副本。解决的是可用性问题,而非存储问题。
  • Verneuil:将数据库拆分为64KB块,使用zstd压缩并附带清单文件,异步复制到S3。块+清单模型类似于turbolite的页面组+清单,但Verneuil是复制工具——你查询的是本地磁盘,而非S3。
  • libSQL/sqld(由Turso提供):SQLite的一个分支,具有虚拟WAL接口。“底部”模式将WAL帧发送到S3。查询是本地进行的;S3用于恢复。

自定义存储引擎

  • sqlite-s3vfs:每个SQLite页面存储为单独的S3对象。支持写入,但每个页面一次PUT操作,每4096页花费0.02美元,而turbolite相同批次(默认64KB页面)仅需0.000005美元。请参见上文的基准测试表以比较冷查询延迟;在相同数据集上,turbolite快7.5–263倍。
  • wa-sqlite-s3vfs:适用于wa-sqlite的TypeScript/浏览器端口。同样的一对象一页面模型,适配WASM/客户端使用。

压缩

  • sqlite_zstd_vfs:将压缩页面作为外部“包装”数据库中的行存储。使用带词典训练的zstd。可与sqlite_web_vfs组合,用于通过HTTP进行压缩范围请求读取。sqlite_web_vfs + sqlite_zstd_vfs的组合可能是turbolite读取路径最接近的现有方案,但它是只读的,并且不会将页面批处理成组。
  • SQLCipher:用于本地SQLite的页面级AES-256加密。无远程存储。

turbolite的独特之处

基准测试

所有基准测试位于benchmark/。请参见benchmark/README.md了解部署场景(本地、Fly.io、EC2)。

tiered-bench二进制程序生成一个社交媒体数据集(用户、帖子、点赞、好友关系),并针对S3在每个缓存级别对查询进行基准测试。

另一个benchmark/bench_s3vfs.py测试工具对sqlite-s3vfs运行相同的查询以进行头对头比较。它通过benchmark/fly-s3vfs.toml部署,并使用与tiered-bench相同的确定性数据集生成器。```bash

Basic benchmark: 100K posts, default settings

TIERED_TEST_BUCKET=my-bucket AWS_ENDPOINT_URL=https://t3.storage.dev
cargo run --features zstd,cloud --bin tiered-bench --release --
--sizes 100000

1M posts, 8 prefetch threads, only interior-level point queries

cargo run --features zstd,cloud --bin tiered-bench --release --
--sizes 1000000 --prefetch-threads 8 --queries post --modes interior

Quick local VFS comparison (no S3 needed)

cargo run --example quick-bench --features encryption --release

root@kitploit:~
关键标志:`--sizes` (行数)、`--ppg` (每组页数)、`--prefetch-threads`、`--prefetch-search`(搜索调度)、`--prefetch-lookup`(查找调度)、`--grouping`(位置或btree)、`--queries`(帖子/个人资料/谁喜欢/互相关注)、`--modes`(无/内部/索引/数据)、`--skip-verify`(在小型机器上跳过 COUNT(*))、`--iterations`、`--plan-aware`(启用前瞻预取)、`--matrix`(扫描调度对)。每个查询的调度:`--post-prefetch`/`--post-lookup`、`--profile-prefetch`/`--profile-lookup` 等(搜索和查找是每个查询独立的)。```bash
# Matrix mode: test 10 schedule pairs x 6 queries at cold level
cargo run --features zstd,cloud --bin tiered-bench --release -- \
    --sizes 1000000 --import auto --plan-aware --matrix --iterations 10

# Tune schedules for your own database and queries
cargo run --features zstd,cloud --bin tiered-tune --release -- \
    --prefix "databases/my-db" \
    --query "SELECT * FROM users WHERE id = ?1" --param 42 \
    --plan-aware --iterations 10

测试```bash

cargo test --features zstd # local VFS tests cargo test --features zstd,cloud # + S3 integration tests cargo test --features zstd,encryption # + encryption tests

root@kitploit:~
## Notes

turbolite 之前的名称是 `sqlite-compress-encrypt-vfs`,简称 `sqlces`。

### 安全模型细节

S3 数据采用 AES-256-GCM 加密,并为每个帧使用唯一的随机 nonce(经过认证、防篡改)。本地文件使用 AES-256-CTR 加密,并采用确定性 nonce(页面号/字节偏移量),从而为静态磁盘攻击者提供机密性。CTR 的确定性 nonce 意味着多快照攻击者可以在已复用的偏移量处恢复明文的异或结果,这与 SQLite 自身 SEE 扩展的权衡一致。本地缓存是临时性的,且可从 S3 重新创建。

## License

Apache-2.0
下载工具
查询类型冷启动 (S3 Express)冷启动 (Tigris)
帖子 + 用户点查询 + 连接86ms172ms
个人资料多表连接 (5 个 JOIN)251ms479ms
谁点了赞索引搜索 + 连接206ms302ms
共同好友多搜索连接19ms49ms
带索引筛选覆盖索引扫描79ms88ms
全表扫描 + 筛选全表扫描476ms532ms
缓存级别已缓存的内容从 S3 获取的内容何时发生
无无所有内容全新启动,空缓存
内部节点B-tree 内部页索引 + 数据页连接打开后的首次查询
索引内部节点 + 索引页仅数据页正常的 turbolite 操作
数据所有内容无等同于本地 SQLite
操作SQLiteturbolite开销
点查询145K/s73K/s2.0x
范围扫描8.8K/s8.3K/s持平
全表扫描56/s60/s持平
INSERT19K/s23K/s持平
按主键 UPDATE40K/s27K/s1.5x
批量 INSERT(事务内)685K/s740K/s持平
参数控制内容默认值
prefetch.threads用于并行 S3 获取的工作线程数max(num_cpus - 1, 1)
cache.pages_per_group每个 S3 对象的页面数,越大 = 更少的 PUT 操作,每次获取更多字节256
cache.gc_enabled在检查点后删除旧的页面组版本true
sync_mode检查点持久性:Durable(检查点中上传 S3)或 LocalThenFlush(延迟上传)Durable
策略何时触发默认调度发生行为
SCAN(前置运行)EQP 显示 SCAN table所有组提前首次读取前批量预取整个表。无需跳转调度。
SEARCH(反应性)EQP 显示 SEARCH ... USING INDEX[0.3, 0.3, 0.4]从第一次未命中开始激进预取;扫描未知的索引部分。
Lookup(反应性)点查询,无 EQP 信息[0.0, 0.0, 0.0]三次空闲跳转,零预取。点查询很少受益于预取。
turbolite原始文件范围GETLitestream VFSsqlite_web_vfs + zstd_vfsmvsqliteGraftsqlite-s3vfs
从S3读取在压缩页面组上进行可寻址范围GET对原始页面进行范围GET对LTX文件进行范围GET对压缩外部数据库进行范围GET对FoundationDB进行KV查找惰性获取4KB页面/变更集每个页面一次GetObject
写入S3检查点(每组一次PUT)无无无是(MVCC)是(异步变更集复制)每个页面一次PUT
压缩可寻址多帧zstd无无zstd(嵌套数据库)zstd增量编码帧式zstd无
加密每页AES-256-GCM无无无无未列出无
预取前瞻 + 跳跃调度无或基本预读LRU缓存自适应合并客户端缓冲区惰性/按需无
内部页面优化检测、固定、单独捆绑无来自LTX尾部的页面索引可选的.dbi文件无未列出无
每次点查询字节数(缓存:索引)约100KB(一个压缩帧)4-64KB(一个原始页面)不定不定不定4KB(一个页面)4KB(一个页面)
每4096页写入成本约0.000005美元(一次PUT)不适用不适用不适用FoundationDB操作批量变更集约0.02美元(4096次PUT)