DuckDB 0.10.0 发布了,带来了重大更新
原文:Announcing DuckDB 0.10.0[1]
翻译:Gemini Pro
校对:alitrack
DuckDB 0.10.0 发布公告
作者:马克·拉斯维尔特和汉内斯·穆勒艾森
日期:2024-02-13
重点提示:DuckDB 团队很高兴地宣布最新版本 DuckDB(0.10.0)发布。此版本以欧洲特有的黑海番鸭[2]命名。
要安装新版本,请访问安装指南[3]。可以在GitHub[4]上找到完整的版本说明。
注意:某些版本和扩展仍在构建中。最新版本可能需要更多时间才能提供。
0.10.0 新增功能
有很多改动无法一一详细讨论,但我们想重点介绍几个特别令人兴奋的功能!
• 0.10.0 新增功能
• 破坏性的 SQL 改动
• 向后兼容性
• 向前兼容性
• CSV 读取器重构
• 固定长度数组
• 多数据库支持
• 密钥管理器
• 临时内存管理器
• 自适应无损浮点压缩 (ALP)
• CLI 改进
• 最后的想法
• 新功能
• 新函数
• 存储改进
• 优化
以下是这些新功能的摘要,包括我们 SQL 方言中旨在默认生成更直观结果的改动。
破坏性 SQL 改动
隐式转换为 VARCHAR[5]。以前,DuckDB 会自动允许在函数绑定期间将任何类型隐式转换为 VARCHAR
。因此,例如,可以在不使用隐式转换的情况下计算整数的子字符串。从本版本开始,您需要在此处使用显式转换。
SELECT substring(42, 1, 1) AS substr;
-- 没有函数与给定的名称和参数类型 'substring(...)' 匹配。
-- 您可能需要添加显式类型转换。
可以使用 old_implicit_casting
设置来还原此行为,例如:
SET old_implicit_casting = true;
SELECT substring(42, 1, 1) AS substr;
┌─────────┐
│ substr │
│ varchar │
├─────────┤
│ 4 │
└─────────┘
文字类型[6]。以前,整数和字符串文字的行为与 INTEGER
和 VARCHAR
类型完全相同。从本版本开始,INTEGER_LITERAL
和 STRING_LITERAL
是具有自己绑定规则的单独类型。
•
INTEGER_LITERAL
类型可以隐式转换为任何适合该值的整数类型•
STRING_LITERAL
类型可以隐式转换为任何其他类型
这使 DuckDB 与 Postgres 保持一致,并使对文字的操作更直观。例如,我们可以将字符串文字与日期进行比较,但我们无法将 VARCHAR
值与日期进行比较。
SELECT d > '1992-01-01' AS result FROM (VALUES (DATE '1992-01-01')) t(d);
┌─────────┐
│ result │
│ boolean │
├─────────┤
│ false │
└─────────┘
SELECT d > '1992-01-01'::VARCHAR FROM (VALUES (DATE '1992-01-01')) t(d);
-- 绑定器错误:无法比较 DATE 类型和 VARCHAR 类型的值 - 需要显式转换
向后兼容性
向后兼容性是指较新版本的 DuckDB 读取由较旧版本的 DuckDB 创建的存储文件的能力。此版本是第一个支持存储格式向后兼容性的 DuckDB 版本。DuckDB v0.10 可以读取和操作由先前 DuckDB 版本(DuckDB v0.9)创建的文件。这是通过实现新的序列化框架实现的[7]。
# 使用 v0.9 写入
$ duckdb_092 v092.db
CREATE TABLE lineitem AS FROM lineitem.parquet;
# 使用 v0.10 读取
$ duckdb_0100 v092.db
SELECT l_orderkey, l_partkey, l_comment FROM lineitem LIMIT 1;
┌────────────┬───────────┬─────────────────────────┐
│ l_orderkey │ l_partkey │ l_comment │
│ int32 │ int32 │ varchar │
├────────────┼───────────┼─────────────────────────┤
│ 1 │ 155190 │ to beans x-ray carefull │
└────────────┴───────────┴─────────────────────────┘
对于未来的 DuckDB 版本,我们的目标是确保从本版本开始发布的任何 DuckDB 版本之后都可以读取由先前版本创建的文件。我们希望确保文件格式完全向后兼容。这使您可以保留存储在 DuckDB 文件中的数据,并保证您将能够读取这些文件,而无需担心文件是使用哪个版本编写的,也无需在版本之间转换文件。
向前兼容性
向前兼容性是指较旧版本的 DuckDB 读取由较新版本的 DuckDB 生成的存储文件的能力。DuckDB v0.9 部分向前兼容 DuckDB v0.10。DuckDB v0.10 创建的某些文件可以由 DuckDB v0.9 读取。
# 使用 v0.10 写入
$ duckdb_0100 v010.db
CREATE TABLE lineitem AS FROM lineitem.parquet;
# 使用 v0.9 读取
$ duckdb_092 v010.db
SELECT l_orderkey, l_partkey, l_comment FROM lineitem LIMIT 1;
┌────────────┬───────────┬─────────────────────────┐
│ l_orderkey │ l_partkey │ l_comment │
│ int32 │ int32 │ varchar │
├────────────┼───────────┼─────────────────────────┤
│ 1 │ 155190 │ to beans x-ray carefull │
└────────────┴───────────┴─────────────────────────┘
向前兼容性在尽力而为的基础上提供。虽然存储格式的稳定性很重要,但我们仍然希望在未来对存储格式进行许多改进和创新。因此,向前兼容性可能会偶尔(部分)中断。
对于此版本,DuckDB v0.9 能够读取 DuckDB v0.10 创建的文件,前提是:
• 数据库文件不包含视图
• 数据库文件不包含新类型(
ARRAY
、UHUGEINT
)• 数据库文件不包含索引(
PRIMARY KEY
、FOREIGN KEY
、UNIQUE
、显式索引)• 数据库文件不包含新的压缩方法(
ALP
)。由于ALP
自动用于压缩FLOAT
和DOUBLE
列,这意味着在实践中,向前兼容性通常不适用于FLOAT
和DOUBLE
列,除非通过配置显式禁用ALP
。
我们预计随着格式的稳定和成熟,这种情况会越来越少见,我们希望在允许 DuckDB 读取由未来 DuckDB 版本编写的文件方面提供更好的保证。
CSV 读取器重构
**CSV 读取器重构[8]。**CSV 读取器在此版本中得到了重大改进。新的 CSV 读取器使用高效的状态机转换来快速处理 CSV 文件。这极大地提高了 CSV 读取器的性能,尤其是在多线程场景中。此外,对于格式错误的 CSV 文件,报告的错误消息应该更清晰。
以下是比较在 M1 Max(10 核)上从 CSV 文件加载 1100 万行纽约市出租车数据集的加载时间的基准:
版本 | 加载时间 |
v0.9.2 | 2.6s |
v0.10.0 | 1.15s |
此外,已经进行了许多优化,使直接对 CSV 文件运行查询的速度也大大提高。以下是比较直接在纽约市出租车 CSV 文件上执行 SELECT COUNT(*)
查询的执行时间的基准:
版本 | 查询时间 |
v0.9.2 | 1.8s |
v0.10.0 | 0.3s |
固定长度数组[9]。 此版本引入了固定长度数组类型。固定长度数组类似于列表,但是,每个值都必须具有相同数量的固定元素。
CREATE TABLE vectors(v DOUBLE[3]);
INSERT INTO vectors VALUES ([1, 2, 3]);
固定长度数组的运行速度比可变长度列表快,因为每个列表元素的大小是预先知道的。此版本还引入了专门针对这些数组运行的函数,例如 array_cross_product
、array_cosine_similarity
和 array_inner_product
。
SELECT array_cross_product(v, [1, 1, 1]) AS result FROM vectors;
┌───────────────────┐
│ result │
│ double[3] │
├───────────────────┤
│ [-1.0, 2.0, -1.0] │
└───────────────────┘
有关更多信息,请参阅文档中的 数组类型页面[10]。
多数据库支持
除了以其自己的格式存储的数据库之外,DuckDB 现在还可以附加 MySQL、Postgres 和 SQLite 数据库。这允许将数据读入 DuckDB 并以方便的方式在这些系统之间移动,因为附加的数据库是完全可用的,看起来就像常规表一样,并且可以以安全的事务方式进行更新。有关多数据库支持的更多信息,请参阅我们的 最新博客文章[11]。
ATTACH 'sqlite:sakila.db' AS sqlite;
ATTACH 'postgres:dbname=postgresscanner' AS postgres;
ATTACH 'mysql:user=root database=mysqlscanner' AS mysql;
密码管理器
DuckDB 与需要访问凭据才能访问数据的几个云存储系统(例如 S3)集成。在当前版本的 DuckDB 中,身份验证信息是通过 DuckDB 设置配置的,例如 SET s3_access_key_id = '...';
。虽然这样做可行,但它有几个缺点。例如,不可能为不同的 S3 存储桶设置不同的凭据,而无需在查询之间修改设置。由于设置不被视为密码,因此也可以使用 duckdb_settings()
查询它们。
通过此版本,DuckDB 添加了一个新的“密钥管理器[12]” 以更好地管理密码。我们现在有一个统一的用户界面,用于管理所有使用它们的后台的密码。可以限定范围的密码,因此不同的存储前缀可以有不同的密码,例如,允许在单个查询中跨组织进行联接。密码也可以持久化,因此无需在每次启动 DuckDB 时都指定它们。
密码是类型的,它们的类型标识它们所属的服务。例如,此版本可以管理 S3、Google Cloud Storage、Cloudflare R2 和 Azure Blob Storage 的密码。对于每种类型,都有一个或多个“密码提供程序”,用于指定如何创建密码。密码还可以具有可选范围,该范围是密码适用的文件路径前缀。在为路径获取密码时,将比较密码范围,并返回与路径匹配的密码。如果存在多个匹配的密码,则选择最长的前缀。
最后,密码可以是临时的或持久的。默认情况下使用临时密码——并且与以前设置的工作方式类似,在 DuckDB 实例的生命周期内存储在内存中。持久密码以未加密的二进制格式存储在 ~/.duckdb/stored_secrets
目录中。在启动 DuckDB 时,会从该目录读取持久密码并自动加载。
例如,要创建一个临时非范围密码以访问 S3,我们现在可以使用以下语法:
CREATE SECRET (
TYPE S3,
KEY_ID 'mykey',
SECRET 'mysecret',
REGION 'myregion');
如果服务类型存在两个密码,则可以使用范围来决定应使用哪一个。例如:
CREATE SECRET secret1 (
TYPE S3,
KEY_ID 'my_key1',
SECRET 'my_secret1',
SCOPE 's3://my-bucket');
CREATE SECRET secret2 (
TYPE S3,
KEY_ID 'my_key2',
SECRET 'my_secret2',
SCOPE 's3://my-other-bucket');
现在,如果用户从 s3://my-other-bucket/something
查询某些内容,则会自动为该请求选择密码 secret2
。
可以使用内置的表生成函数列出密码,例如,使用 FROM duckdb_secrets();
。敏感信息将被涂黑。
为了在 DuckDB 数据库实例之间持久化密码,我们现在可以使用 CREATE PERSISTENT SECRET
命令,例如:
CREATE PERSISTENT SECRET my_persistent_secret (
TYPE S3,
KEY_ID 'key',
SECRET 'secret');
如前所述,这会将密码(未加密,因此要小心)写入 ~/.duckdb/stored_secrets
目录。
有关更多信息,请参阅文档中的 创建密码页面[13]。
临时内存管理器
DuckDB 支持大于内存的操作,这意味着占用大量内存的操作(例如聚合和联接)可以将其部分中间结果卸载到磁盘上的临时文件中,以防没有足够的可用内存。
以前,如果这些操作的内存使用量达到可用内存的 60% 左右(由内存限制定义),则这些操作会开始卸载到磁盘。如果同时发生这些操作中的一个,则这种方法效果很好。如果同时发生多个内存密集型操作,它们的内存使用量总和可能会超过内存限制,从而导致 DuckDB 抛出错误。
此版本引入了所谓的“临时内存管理器[14]”,它管理并发操作的临时内存。它的工作原理如下:内存密集型操作向临时管理器注册自身。根据线程数和当前内存限制,管理器保证每个注册都具有一定的最小内存量。然后,内存密集型操作会传达它们当前希望使用的内存量。管理器可以批准此操作或以减少的分配量进行响应。在减少分配的情况下,操作员需要动态减少其内存需求,例如通过切换算法。
例如,如果可用内存不足,哈希联接可能会调整其操作并执行分区哈希联接,而不是完全在内存中执行。
这里有一个例子:
PRAGMA memory_limit='5GB';
SET temp_directory='/tmp/duckdb_temporary_memory_manager';
CREATE TABLE tbl AS
SELECT range i,
range j
FROM range(100_000_000);
SELECT max(i),
max(t1.j),
max(t2.j),
max(t3.j),
FROM tbl AS t1
JOIN tbl AS t2 USING (i)
JOIN tbl AS t3 USING (i);
请注意,此处必须设置临时目录,因为操作员实际上需要将数据卸载到磁盘才能在给定此内存限制的情况下完成此查询。
使用新版本 0.10.0,此查询在大约 5 秒内在 MacBook 上完成,而在以前版本上会因 Error: Out of Memory Error: failed to pin block of size ...
而出错。
自适应无损浮点压缩 (ALP)
众所周知,浮点数很难有效压缩,无论是压缩比还是压缩和解压缩速度。过去,DuckDB 支持当时最先进的“Chimp[15]”和“Patas[16]”压缩方法。事实证明,这些并不是浮点压缩的最后手段。研究人员 Azim Afroozeh[17]、Leonard Kuffo[18] 和(唯一)Peter Boncz[19] 最近发表了一篇题为“ALP:自适应无损浮点压缩[20]”的论文在 SIGMOD,一个顶级学术数据管理研究会议上。在一个不常见但非常值得称赞的举动中,他们还向 DuckDB 发送了 拉取请求[21]。新的压缩方案取代了 Chimp 和 Patas。在 DuckDB 内部,ALP 在解压缩时比 Patas 快 x2-4 倍,压缩比达到 两倍(有时甚至更高)。
压缩 | 加载 | 查询 | 大小 |
ALP | 0.434 秒 | 0.02 秒 | 184 MB |
Patas | 0.603 秒 | 0.08 秒 | 275 MB |
未压缩 | 0.316 秒 | 0.012 秒 | 489 MB |
作为用户,您无需执行任何操作即可使用新的 ALP 压缩方法,DuckDB 将在检查点期间自动决定是否对特定数据集使用 ALP 有利。
CLI 改进
命令行客户端在此版本中进行了大量工作。特别是,多行编辑已成为默认模式,并得到了许多改进。查询历史记录现在也是多行的。语法突出显示已得到改进[22] - 缺少的括号和未闭合的引号突出显示为错误,并且当光标移到匹配的括号上时,它们会突出显示。与 read-line 的兼容性也已大大扩展[23]。
有关更多信息,请参阅扩展的 CLI 文档[24]。
最后的想法
这些是一些亮点 - 但此版本中还有更多功能和改进。以下是更多亮点。可以在 GitHub 上找到完整的版本说明[25]。
新特性
• COMMENT ON[26]
• COPY FROM DATABASE[27]
• UHUGEINT 类型[28]
• 窗口 EXCLUDE[29] 和 窗口 DISTINCT[30] 支持
• Parquet 加密支持[31]
• Lambda 参数的索引[32]
•
EXCEPT ALL
/INTERSECT ALL
[33]•
DESCRIBE
/SHOW
/SUMMARIZE
作为子查询[34]• 支持相关子查询中的递归 CTE[35]
新函数
•
parquet_kv_metadata
[36] 和parquet_file_metadata
[37] 函数•
read_text
/read_blob
表函数[38]•
list_reduce
[39]、list_where
,list_zip
,list_select
,list_grade_up
[40]
存储改进
• 清理部分删除[41]
• 并行检查点[42]
• 校验和 WAL[43]
优化
• 并行流式查询结果[44]
• 结构过滤下推[45]
•
FIRST(x ORDER BY y)
优化[46]
我们要感谢所有贡献者为改进 DuckDB 所做的辛勤工作。
引用链接
[1]
Announcing DuckDB 0.10.0: https://duckdb.org/2024/02/13/announcing-duckdb-0100.html[2]
黑海番鸭: https://baike.baidu.com/item/%E9%BB%91%E6%B5%B7%E7%95%AA%E9%B8%AD/10750642[3]
安装指南: https://duckdb.org/docs/installation[4]
GitHub: https://github.com/duckdb/duckdb/releases/tag/v0.10.0[5]
隐式转换为 VARCHAR: https://github.com/duckdb/duckdb/pull/10115[6]
文字类型: https://github.com/duckdb/duckdb/pull/10194[7]
这是通过实现新的序列化框架实现的: https://github.com/duckdb/duckdb/pull/8156[8]
CSV 读取器重构: https://github.com/duckdb/duckdb/pull/10209[9]
固定长度数组: https://github.com/duckdb/duckdb/pull/8983[10]
数组类型页面: https://duckdb.org/docs/sql/data_types/array[11]
最新博客文章: https://duckdb.org/2024/01/26/multi-database-support-in-duckdb[12]
密钥管理器: https://github.com/duckdb/duckdb/pull/10042[13]
创建密码页面: https://duckdb.org/docs/sql/statements/create_secret[14]
临时内存管理器: https://github.com/duckdb/duckdb/pull/10147[15]
Chimp: https://github.com/duckdb/duckdb/pull/4878[16]
Patas: https://github.com/duckdb/duckdb/pull/5044[17]
Azim Afroozeh: https://www.cwi.nl/en/people/azim-afroozeh/[18]
Leonard Kuffo: https://www.cwi.nl/en/people/leonardo-xavier-kuffo-rivero/[19]
Peter Boncz: https://homepages.cwi.nl/~boncz/[20]
ALP:自适应无损浮点压缩: https://dl.acm.org/doi/pdf/10.1145/3626717[21]
拉取请求: https://github.com/duckdb/duckdb/pull/9635[22]
语法突出显示已得到改进: https://duckdb.org/docs/api/cli/syntax_highlighting[23]
大大扩展: https://duckdb.org/docs/api/cli/editing[24]
扩展的 CLI 文档: https://duckdb.org/docs/api/cli/overview[25]
找到完整的版本说明: https://github.com/duckdb/duckdb/releases/tag/v0.10.0[26]
COMMENT ON: https://github.com/duckdb/duckdb/pull/10372[27]
COPY FROM DATABASE: https://github.com/duckdb/duckdb/pull/9765[28]
UHUGEINT 类型: https://github.com/duckdb/duckdb/pull/8635[29]
窗口 EXCLUDE: https://github.com/duckdb/duckdb/pull/9220[30]
窗口 DISTINCT: https://github.com/duckdb/duckdb/pull/9754[31]
Parquet 加密支持: https://github.com/duckdb/duckdb/pull/9392[32]
Lambda 参数的索引: https://github.com/duckdb/duckdb/pull/8851[33]
EXCEPT ALL
/INTERSECT ALL
: https://github.com/duckdb/duckdb/pull/9636[34]
DESCRIBE
/SHOW
/SUMMARIZE
作为子查询: https://github.com/duckdb/duckdb/pull/10210[35]
支持相关子查询中的递归 CTE: https://github.com/duckdb/duckdb/pull/10357[36]
parquet_kv_metadata
: https://github.com/duckdb/duckdb/pull/9126[37]
parquet_file_metadata
: https://github.com/duckdb/duckdb/pull/9793[38]
read_text
/read_blob
表函数: https://github.com/duckdb/duckdb/pull/10376[39]
list_reduce
: https://github.com/duckdb/duckdb/pull/9909[40]
list_where
, list_zip
, list_select
, list_grade_up
: https://github.com/duckdb/duckdb/pull/8907[41]
清理部分删除: https://github.com/duckdb/duckdb/pull/9931[42]
并行检查点: https://github.com/duckdb/duckdb/pull/9999[43]
校验和 WAL: https://github.com/duckdb/duckdb/pull/10126[44]
并行流式查询结果: https://github.com/duckdb/duckdb/pull/10245[45]
结构过滤下推: https://github.com/duckdb/duckdb/pull/10314[46]
FIRST(x ORDER BY y)
优化: https://github.com/duckdb/duckdb/pull/10347