兼容性
向后兼容性
向后兼容性指的是较新版本的 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 读取。
向前兼容性是尽力而为提供的。尽管存储格式的稳定性很重要,但我们未来仍希望对存储格式进行许多改进和创新。因此,向前兼容性有时可能会(部分)中断。
如何在存储格式之间迁移
当您更新 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 读取。
如果您连接到 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
(或未来版本)兼容。
在存储版本之间转换
要在 DuckDB v1.2.0+ 中将新格式转换为旧格式以实现兼容性,请使用以下序列
ATTACH 'file1.db';
ATTACH 'converted_file.db' (STORAGE_VERSION 'v1.0.0');
COPY FROM DATABASE file1 TO converted_file;
存储头部
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 上的更改日志。要查看更改每个存储版本的提交,请参阅提交日志。
存储版本 | DuckDB 版本 |
---|---|
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 使用轻量级压缩。请注意,压缩仅应用于持久性数据库,而不应用于内存实例。
压缩算法
DuckDB 支持的压缩算法包括以下几种
- 常量编码
- 游程编码 (RLE)
- 位打包
- 参考帧编码 (FOR)
- 字典编码
- 快速静态符号表 (FSST) – VLDB 2020 论文
- 自适应无损浮点压缩 (ALP) – SIGMOD 2024 论文
- Chimp – VLDB 2022 论文
- Patas
磁盘使用情况
DuckDB 格式的磁盘使用情况取决于多种因素,包括数据类型和数据分布、所使用的压缩方法等。粗略估计,将 100 GB 未压缩的 CSV 文件加载到 DuckDB 数据库文件中将需要 25 GB 磁盘空间,而加载 100 GB 的 Parquet 文件将需要 120 GB 磁盘空间。
行组
DuckDB 的存储格式将数据存储在行组中,即数据的水平分区。这个概念等同于 Parquet 的行组。DuckDB 中的多项功能,包括并行性和压缩,都基于行组。
故障排除
打开不兼容数据库文件时的错误消息
当您打开一个由不同于您正在使用的 DuckDB 版本写入的数据库文件时,可能会出现以下错误消息
Error: unable to open database "...": Serialization Error: Failed to deserialize: ...
该消息表示数据库文件是用较新版本的 DuckDB 创建的,并且使用了与用于读取该文件的 DuckDB 版本向后不兼容的功能。
有两种可能的解决方案
- 将您的 DuckDB 版本更新到最新的稳定版本。
- 使用最新版本的 DuckDB 打开数据库,将其导出为标准格式(例如 Parquet),然后使用任何版本的 DuckDB 导入。有关详细信息,请参阅
EXPORT/IMPORT DATABASE
语句。