⌘+k ctrl+k
1.3 (稳定版)
搜索快捷键 cmd + k | ctrl + k
模式

类型

为列编码使用正确的数据类型(例如,BIGINTDATEDATETIME)非常重要。虽然始终可以使用字符串类型(VARCHAR等)来编码更具体的值,但我们不推荐这样做。字符串占用更多空间,并且在过滤、连接和聚合等操作中处理速度较慢。

加载 CSV 文件时,您可以利用 CSV 读取器的自动检测机制来获取 CSV 输入的正确数据类型。

如果您在内存受限的环境中运行,使用较小的数据类型(例如,TINYINT)可以减少完成查询所需的内存和磁盘空间量。DuckDB 的位打包压缩意味着存储在较大类型中的小值在磁盘上不会占用更大的空间,但在处理过程中会占用更多内存。

最佳实践 创建列时,请尽可能使用限制性最强的数据类型。避免使用字符串来编码更具体的数据项。

微基准测试:使用时间戳

我们使用LDBC Comment 表中比例因子为 300 的 creationDate来说明聚合速度的差异。该表包含约 5.54 亿个无序时间戳值。我们运行一个简单的聚合查询,以两种配置从时间戳中返回月中平均日期。

首先,我们使用 DATETIME 类型编码值,并使用extract datetime 函数运行查询。

SELECT avg(extract('day' FROM creationDate)) FROM Comment;

其次,我们使用 VARCHAR 类型并进行字符串操作。

SELECT avg(CAST(creationDate[9:10] AS INTEGER)) FROM Comment;

微基准测试结果如下:

列类型 存储大小 查询时间
DATETIME 3.3 GB 0.9 秒
VARCHAR 5.2 GB 3.9 秒

结果表明,使用 DATETIME 值可实现更小的存储大小和更快的处理速度。

微基准测试:基于字符串的连接

我们通过对比例因子为 100 的 LDBC Comment 表执行自连接,来说明在不同类型上执行连接操作所造成的差异。该表包含用作每行 id 属性的 64 位整数标识符。我们执行以下连接操作:

SELECT count(*) AS count
FROM Comment c1
JOIN Comment c2 ON c1.ParentCommentId = c2.id;

在第一个实验中,我们使用了正确(限制性最强)的数据类型,即 idParentCommentId 列都定义为 BIGINT。在第二个实验中,我们用 VARCHAR 类型定义了所有列。尽管两次实验的查询结果相同,但它们的运行时间却显著不同。以下结果表明,在 BIGINT 列上进行连接比在编码相同值的 VARCHAR 类型列上执行相同连接快约 1.8 倍。

连接列有效负载类型 连接列模式类型 示例值 查询时间
BIGINT BIGINT 70368755640078 1.2 秒
BIGINT VARCHAR '70368755640078' 2.1 秒

最佳实践 避免将数值表示为字符串,特别是当您打算对其执行连接等操作时。

约束

DuckDB 允许定义约束,例如 UNIQUEPRIMARY KEYFOREIGN KEY。这些约束对于确保数据完整性有益,但它们会对加载性能产生负面影响,因为它们需要构建索引和执行检查。此外,它们极少提高查询性能,因为 DuckDB 不依赖这些索引进行连接和聚合操作(有关更多详细信息,请参阅索引)。

最佳实践 除非您的目标是确保数据完整性,否则不要定义约束。

微基准测试:主键的影响

我们使用比例因子为 300 的 LDBC Comment 表来说明使用主键的效果。该表包含约 5.54 亿条记录。在第一个实验中,我们不带主键创建模式,然后加载数据。在第二个实验中,我们主键创建模式,然后加载数据。在第三种情况下,我们不带主键创建模式,加载数据,然后添加主键约束。在所有情况下,我们都从 .csv.gz 文件中获取数据,并测量执行加载所需的时间。

操作 执行时间
带主键加载 461.6 秒
不带主键加载 121.0 秒
不带主键加载,然后添加主键 242.0 秒

对于此数据集,主键仅对高度选择性的查询(例如在单个标识符上进行过滤时)产生(小的)积极影响。定义主键(或索引)对连接和聚合操作符没有影响。

最佳实践 为了获得最佳的批量加载性能,请避免使用主键约束。如果它们是必需的,请在批量加载步骤之后定义它们。