兼容性
向后兼容性
向后兼容性是指较新版本的 DuckDB 读取由较旧版本 DuckDB 创建的存储文件的能力。0.10 版本是 DuckDB 第一个在存储格式上支持向后兼容的版本。DuckDB v0.10 可以读取并操作由前一个 DuckDB 版本(即 DuckDB v0.9)创建的文件。
对于未来的 DuckDB 版本,我们的目标是确保从该版本开始,任何之后发布的 DuckDB 版本都能读取由先前版本创建的文件。我们希望确保文件格式完全向后兼容。这使您可以保留存储在 DuckDB 文件中的数据,并保证您无需担心文件是用哪个版本编写的,也无需在不同版本之间转换文件,即可读取这些文件。
向前兼容性
向前兼容性是指较旧版本的 DuckDB 读取由较新版本 DuckDB 产生的存储文件的能力。DuckDB v0.9 对 DuckDB v0.10 具有部分向前兼容性。由 DuckDB v0.10 创建的某些文件可以被 DuckDB v0.9 读取。
向前兼容性基于尽力而为(best effort)的原则提供。虽然存储格式的稳定性很重要,但未来我们仍希望对存储格式进行许多改进和创新。因此,向前兼容性偶尔可能会(部分)中断。
如何在存储格式之间迁移
当您更新 DuckDB 并打开旧的数据库文件时,您可能会遇到有关存储格式不兼容的错误消息,并指向此页面。要将您的数据库迁移到较新的格式,您只需要旧版本和新版本的 DuckDB 可执行文件。
使用旧版本的 DuckDB 打开您的数据库文件,并运行 SQL 语句 EXPORT DATABASE 'tmp'。这允许您将当前使用中的整个数据库状态保存到 tmp 文件夹内。tmp 文件夹的内容会被覆盖,因此请选择一个空或尚不存在的位置。然后,启动较新的 DuckDB 并执行 IMPORT DATABASE 'tmp'(指向之前填充的文件夹)以加载数据库,随后即可将其保存为您指定给 DuckDB 的文件中。
实现此目的的 Bash 脚本(需根据文件名和可执行文件路径进行适配)如下
/older/duckdb mydata.old.db -c "EXPORT DATABASE 'tmp'"
/newer/duckdb mydata.new.db -c "IMPORT DATABASE 'tmp'"
在此之后,mydata.old.db 将保持旧格式,mydata.new.db 将包含相同的数据但采用较新 DuckDB 版本可访问的格式,而文件夹 tmp 将以通用格式将相同数据保存为不同的文件。
查看 EXPORT 文档以获取有关语法的更多详细信息。
显式存储版本
DuckDB v1.2.0 引入了 STORAGE_VERSION 选项,允许明确指定存储版本。使用此功能,您可以选择加入较新的、向前不兼容的功能。
ATTACH 'file.db' (STORAGE_VERSION 'v1.2.0');
此设置指定了能够读取该数据库文件的最低 DuckDB 版本。当使用此选项写入数据库文件时,生成的那些文件无法被低于指定版本的旧版 DuckDB 打开。它们可以被指定版本以及所有更高版本的 DuckDB 读取。
如果您附加(attach)到 DuckDB 数据库,可以使用以下命令查询存储版本
SELECT database_name, tags FROM duckdb_databases();
这将显示存储版本
┌───────────────┬───────────────────────────────────┐
│ database_name │ tags │
│ varchar │ map(varchar, varchar) │
├───────────────┼───────────────────────────────────┤
│ file1 │ {storage_version=v1.2.0} │
│ file2 │ {storage_version=v1.0.0 - v1.1.3} │
│ ... │ ... │
└───────────────┴───────────────────────────────────┘
这意味着 file2 可以被过去的 DuckDB 版本打开,而 file1 仅与 v1.2.0(或未来版本)兼容。
storage_compatibility_version 配置选项也可用于指定要使用的存储版本。它可以通过多种方式指定,例如在连接时使用 Python 绑定,如下所示
duckdb.connect("file.db", config={'storage_compatibility_version': 'latest'})
当使用 命令行客户端时,可以使用 -storage-version 选项指定存储版本。
在存储版本之间转换
要为了兼容性从新格式转换为旧格式,请在 DuckDB v1.2.0+ 中使用以下序列
ATTACH 'file1.db';
ATTACH 'converted_file.db' (STORAGE_VERSION 'v1.0.0');
COPY FROM DATABASE file1 TO converted_file;
存储头(Storage Header)
DuckDB 文件以一个 uint64_t 开头,其中包含主头的校验和,后跟四个魔数字节(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)。
| 存储版本 | DuckDB 版本 |
|---|---|
| 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 及更早版本 |
压缩
DuckDB 使用轻量级压缩。默认情况下,压缩仅应用于持久化数据库,不应用于内存中实例。要开启内存数据库的压缩,请在 ATTACH 中使用 COMPRESS 选项。
请注意,可用的压缩算法取决于所使用的存储版本,因此您可能需要显式设置存储版本以使用所有压缩算法。
压缩算法
DuckDB 支持的压缩算法包括以下内容
- 常量编码(Constant Encoding)
- 游程编码(RLE)
- 位填充(Bit Packing)
- 基准偏移(FOR)
- 字典编码(Dictionary Encoding)
- 快速静态符号表 (FSST) – VLDB 2020 论文
- 自适应无损浮点压缩 (ALP) – SIGMOD 2024 论文
- Chimp – VLDB 2022 论文
- Patas
- Zstd
磁盘使用情况
DuckDB 格式的磁盘使用量取决于许多因素,包括数据类型、数据分布、所使用的压缩方法等。粗略估算,将 100 GB 的未压缩 CSV 文件加载到 DuckDB 数据库文件中需要 25 GB 的磁盘空间,而加载 100 GB 的 Parquet 文件则需要 120 GB 的磁盘空间。
行组(Row Groups)
DuckDB 的存储格式将数据存储在行组中,即数据的水平分区。此概念等同于 Parquet 的行组。DuckDB 中的多项功能,包括并行处理和压缩,都是基于行组的。
行组大小可以作为 ATTACH 语句的一个选项进行指定
ATTACH '/tmp/somefile.db' AS db (ROW_GROUP_SIZE 16384);
故障排除
打开不兼容数据库文件时的错误消息
当打开由与您当前使用的版本不同的 DuckDB 版本编写的数据库文件时,可能会出现以下错误消息
Error: unable to open database "...": Serialization Error: Failed to deserialize: ...
该消息意味着数据库文件是由较新的 DuckDB 版本创建的,并且使用了与用于读取文件的 DuckDB 版本向后不兼容的功能。
有两种潜在的解决方法
- 将您的 DuckDB 版本更新到最新的稳定版本。
- 使用最新版本的 DuckDB 打开数据库,将其导出为标准格式(例如 Parquet),然后将其导入到任何版本的 DuckDB 中。详情请参阅
EXPORT/IMPORT DATABASE语句。