名称: 存储格式 描述: 在TursoDB中使用的SQLite文件格式、B树、页面、单元格、溢出、空闲列表
存储格式指南
数据库文件结构
┌─────────────────────────────┐
│ 第1页:头文件 + 架构 │ ← 前100字节 = 数据库头文件
├─────────────────────────────┤
│ 第2页..N:B树页面 │ ← 表和索引
│ 溢出页面 │
│ 空闲列表页面 │
└─────────────────────────────┘
页面大小:2的幂,512-65536字节。默认4096。
数据库头文件(前100字节)
| 偏移量 | 大小 | 字段 |
|---|---|---|
| 0 | 16 | 魔数:"SQLite format 3\0" |
| 16 | 2 | 页面大小(大端序) |
| 18 | 1 | 写入格式版本(1=回滚,2=WAL) |
| 19 | 1 | 读取格式版本 |
| 24 | 4 | 变更计数器 |
| 28 | 4 | 数据库大小(页数) |
| 32 | 4 | 第一个空闲列表主干页 |
| 36 | 4 | 总空闲列表页数 |
| 40 | 4 | 架构Cookie |
| 56 | 4 | 文本编码(1=UTF8,2=UTF16LE,3=UTF16BE) |
所有多字节整数:大端序。
页面类型
| 标志 | 类型 | 目的 |
|---|---|---|
| 0x02 | 内部索引 | 索引B树内部节点 |
| 0x05 | 内部表 | 表B树内部节点 |
| 0x0a | 叶索引 | 索引B树叶节点 |
| 0x0d | 叶表 | 表B树叶节点 |
| - | 溢出 | 负载超过单元格容量 |
| - | 空闲列表 | 未使用页面(主干或叶) |
B树结构
两种B树类型:
- 表B树:64位行ID键,存储行数据
- 索引B树:任意键(索引列 + 行ID)
内部页面: [ptr0] key1 [ptr1] key2 [ptr2] ...
│ │ │
▼ ▼ ▼
子页面 子页面 子页面
叶页面: key1:数据 key2:数据 key3:数据 ...
第1页始终是sqlite_schema表的根。
单元格格式
表叶单元格
[负载大小: 可变整数] [行ID: 可变整数] [负载] [溢出指针: u32?]
表内部单元格
[左子页面: u32] [行ID: 可变整数]
索引单元格
类似,但键是任意(列 + 行ID),不仅仅是行ID。
记录格式(负载)
[头文件大小: 可变整数] [类型1: 可变整数] [类型2: 可变整数] ... [数据1] [数据2] ...
序列类型:
| 类型 | 含义 |
|---|---|
| 0 | NULL |
| 1-4 | 1/2/3/4字节有符号整数 |
| 5 | 6字节有符号整数 |
| 6 | 8字节有符号整数 |
| 7 | IEEE 754浮点数 |
| 8 | 整数0 |
| 9 | 整数1 |
| ≥12 偶数 | BLOB,长度=(N-12)/2 |
| ≥13 奇数 | 文本,长度=(N-13)/2 |
溢出页面
当负载超过阈值时,超额部分存储在溢出链中:
[下一页: u32] [数据...]
最后一页下一页=0。
空闲列表
主干页的链表,每个包含叶页码:
主干: [下一个主干: u32] [叶数: u32] [叶页面: u32...]
Turso实现
关键文件:
core/storage/sqlite3_ondisk.rs- 磁盘上格式,PageType枚举core/storage/btree.rs- B树操作(大文件)core/storage/pager.rs- 页面管理core/storage/buffer_pool.rs- 页面缓存
调试存储
# 完整性检查
cargo run --bin tursodb test.db "PRAGMA integrity_check;"
# 页面计数
cargo run --bin tursodb test.db "PRAGMA page_count;"
# 空闲列表信息
cargo run --bin tursodb test.db "PRAGMA freelist_count;"