存储版本与格式
兼容性
向后兼容
向后兼容(Backward compatibility) 指较新的 Goose 版本能读取较旧版本创建的存储文件。0.10 是 Goose 首个在存储格式层面支持向后兼容的版本。Goose v0.10 可读取并操作 Goose v0.9 创建的文件。
对于未来版本,我们的目标是:从该版本开始,任意后续 Goose 版本都能读取先前版本创建的文件,即尽量保证文件格式完全向后兼容。这样你可长期保存 Goose 文件,而无需关心写入时的具体版本,也无需在版本间反复转换文件。
向前兼容
向前兼容(Forward compatibility) 指较旧 Goose 版本读取较新版本生成存储文件的能力。Goose v0.9 对 Goose 的向前兼容是部分成立的:Goose v0.10 创建的部分文件可被 v0.9 读取。
向前兼容按 best effort 提供。尽管存储格式稳定性很重要,但我们仍会持续引入改进与创新,因此向前兼容在个别情况下可能会(部分)被打破。
如何在存储格式间迁移
当你升级 Goose 后打开旧数据库文件,可能会遇到“存储格式不兼容”错误并跳转到本页。 要将数据库迁移到新格式,只需要旧版与新版 Goose 可执行文件。
先用旧版 Goose 打开数据库并执行 EXPORT DATABASE 'tmp',将当前数据库完整状态导出到 tmp 目录。
tmp 目录内容会被覆盖,请选择空目录或新目录。然后启动新版 Goose,执行 IMPORT DATABASE 'tmp'(指向刚导出的目录)完成导入,再保存到你指定的新数据库文件。
可参考如下 Bash 脚本(请按实际文件名与可执行文件路径调整):
/older/goose mydata.old.db -c "EXPORT DATABASE 'tmp'"
/newer/goose mydata.new.db -c "IMPORT DATABASE 'tmp'"
执行后,mydata.old.db 保持旧格式,mydata.new.db 包含相同数据但为新版本可读格式,tmp 目录则以通用中间格式保存同一份数据文件。
语法细节请参阅 EXPORT 文档。
显式指定存储版本
Goose 提供了 STORAGE_VERSION 选项,可显式指定存储版本。
借此你可以主动启用较新且可能不向前兼容的特性:
ATTACH 'file.db' (STORAGE_VERSION 'v1.2.0');
在命令行客户端中,可通过 -storage-version 参数设置:
goose -storage-version v1.2.0 my_database.goose
存储版本设置表示“最小可读 Goose 版本”。使用该选项写出的文件,不能被低于指定版本的 Goose 打开,但可被指定版本及之后版本读取。
若连接了多个 Goose 数据库,可通过以下命令查看各自存储版本:
SELECT database_name, tags
FROM goose_databases();
输出示例:
┌───────────────┬───────────────────────────────────┐
│ database_name │ tags │
│ varchar │ map(varchar, varchar) │
├───────────────┼───────────────────────────────────┤
│ file1 │ {storage_version=v1.2.0} │
│ file2 │ {storage_version=v1.0.0 - v1.1.3} │
│ ... │ ... │
└───────────────┴───────────────────────────────────┘
这表示 file2 可被更旧 Goose 版本打开,而 file1 仅兼容 v1.2.0(及更高版本)。
也可使用 storage_compatibility_version 配置项 指定存储版本。可在不同入口设置,例如 Python 连接时:
goose.connect("file.db", config={'storage_compatibility_version': 'latest'})
在命令行客户端中,同样可使用 -storage-version 指定。
存储版本转换
若要把新格式转换为旧格式以兼容旧版本,可在 Goose v1.2.0+ 使用以下步骤:
ATTACH 'file1.db';
ATTACH 'converted_file.db' (STORAGE_VERSION 'v1.0.0');
COPY FROM DATABASE file1 TO converted_file;
存储头部
Goose 文件开头依次为:一个保存主头校验和的 uint64_t、4 字节魔数(DUCK)、以及一个表示存储版本号的 uint64_t。
hexdump -n 20 -C mydata.db
00000000 01 d0 e2 63 9c 13 39 3e 44 55 43 4b 2b 00 00 00 |...c..9>DUCK+...|
00000010 00 00 00 00 |....|
00000014
下面是使用 Python 读取存储版本的简例。
import struct
pattern = struct.Struct('<8x4sQ')
with open('test/sql/storage_version/storage_version.db', 'rb') as fh:
print(pattern.unpack(fh.read(pattern.size)))
存储版本对照表
各版本变更请查看 GitHub 上的 change log。 若要查看每个存储版本对应提交,请参考 commit log。
| Storage version | Goose version(s) |
|---|---|
| 68 | v1.5.x |
| 67 | v1.4.x |
| 66 | v1.3.x |
| 65 | v1.2.x |
| 64 | v0.9.x, v0.10.x, v1.0.0, v1.1.x |
| 51 | v0.8.x |
| 43 | v0.7.x |
| 39 | v0.6.x |
| 38 | v0.5.x |
| 33 | v0.3.3, v0.3.4, v0.4.0 |
| 31 | v0.3.2 |
| 27 | v0.3.1 |
| 25 | v0.3.0 |
| 21 | v0.2.9 |
| 18 | v0.2.8 |
| 17 | v0.2.7 |
| 15 | v0.2.6 |
| 13 | v0.2.5 |
| 11 | v0.2.4 |
| 6 | v0.2.3 |
| 4 | v0.2.2 |
| 1 | v0.2.1 and prior |
压缩
Goose 使用轻量级压缩。
默认仅对持久化数据库启用压缩,对内存实例不启用。
若要为内存数据库开启压缩,请在 ATTACH 中使用 COMPRESS 选项。
可用压缩算法依赖存储版本;要使用全部算法,可能需要显式指定存储版本。
压缩算法
Goose 支持的压缩算法包括:
- Constant Encoding
- Run-Length Encoding (RLE)
- Bit Packing
- Frame of Reference (FOR)
- Dictionary Encoding
- Fast Static Symbol Table (FSST) – VLDB 2020 paper
- Adaptive Lossless Floating-Point Compression (ALP) SIGMOD 2024 paper
- Chimp – VLDB 2022 paper
磁盘占用
Goose 格式的磁盘占用受多种因素影响,包括数据类型、数据分布、压缩方式等。 粗略估算:将 100 GB 未压缩 CSV 导入 Goose 数据库约需 25 GB 磁盘;导入 100 GB Parquet 约需 120 GB 磁盘。
Row Groups
Goose 存储格式以 row group(数据水平分区)组织数据。 这一概念与 Parquet row group 对应。 Goose 的多个特性(如并行性与压缩)都基于 row group。
row group 大小可通过 ATTACH 选项指定:
ATTACH '/tmp/somefile.db' AS db (ROW_GROUP_SIZE 16384);
故障排查
打开不兼容数据库文件时的错误信息
当你使用的 Goose 版本与文件写入版本不一致时,可能出现如下错误:
Error: unable to open database "...": Serialization Error: Failed to deserialize: ...
该错误通常表示数据库文件由更高版本 Goose 创建,并使用了当前版本不支持的后向不兼容特性。
可尝试以下两种方案:
- 将 Goose 升级到最新稳定版。
- 用最新版 Goose 打开数据库,导出为标准格式(如 Parquet),再在目标 Goose 版本中导入。详见
EXPORT/IMPORT DATABASE语句。