DuckLake — 基于DuckDB的开源湖仓

💡 原文英文,约2100词,阅读约需8分钟。
📝

内容提要

DuckLake是一种基于DuckDB扩展的湖仓格式,将元数据与数据分离:目录(本地文件或PostgreSQL)存储表结构、快照和文件指针,Parquet文件存储实际数据。支持本地运行及PostgreSQL+MinIO生产部署,通过分区将文件组织在S3中。查询时利用目录中的列统计进行分区剪枝和谓词下推,可跳过无关文件,显著提升查询效率。

🔎

延伸解读

目录与数据分离的设计价值

DuckLake将元数据与数据分离,目录(本地文件或PostgreSQL)存储表结构、文件指针和列统计,而数据以Parquet文件存储。这种设计使得查询时无需扫描对象存储即可通过目录进行文件剪枝,显著提升效率。同时,数据文件为纯Parquet,任何支持Parquet的工具都能独立访问,增强了互操作性。

分区键选择与剪枝效果

分区键的选择直接影响查询性能。文章示例显示,按tenant_id和event_date分区后,仅过滤event_date也能剪枝到3个文件,说明DuckLake对每个分区列独立剪枝,不依赖前导键。但分区过多可能导致小文件问题,需权衡剪枝收益与文件数量。

并发写入与生产部署考量

本地.ducklake文件使用排他锁,不支持并发写入,而PostgreSQL作为目录后端可支持多进程同时写入,数据文件不可变也避免了存储冲突。生产环境建议采用PostgreSQL+MinIO部署,但需注意S3兼容存储的配置(如端点、URL风格)和网络延迟。

Q&A

DuckLake是什么?它和传统数据库以及Iceberg等湖仓格式有什么不同?

DuckLake是一个基于DuckDB扩展的湖仓格式,它将元数据与数据分离:元数据(表结构、快照、文件指针等)存储在目录中(本地.ducklake文件或PostgreSQL),而实际数据存储在Parquet文件中。与传统数据库不同,DuckLake不将元数据和数据存储在一起;与Iceberg等湖仓格式相比,DuckLake使用关系数据库作为目录,而不是JSON或Avro清单文件。

DuckLake的目录中存储哪些信息?

DuckLake的目录存储表定义(模式、列类型、分区键)、数据文件注册表(哪些Parquet文件属于哪个表、行数和文件大小)、列统计信息(每个文件每列的最小/最大值,用于查询时剪枝)以及快照(每次更改的完整版本历史,支持时间旅行)。

如何在本地运行DuckLake?

本地运行DuckLake需要安装并加载扩展(INSTALL ducklake; LOAD ducklake;),然后设置数据内联行数限制为0(SET ducklake_default_data_inlining_row_limit = 0;),接着使用ATTACH语句附加目录,指定元数据路径(如'ducklake:step1_metadata.ducklake')和数据路径(DATA_PATH)。之后就可以创建表并插入数据。

DuckLake如何支持并发写入?本地文件锁有什么限制?

本地.ducklake文件使用排他文件锁,不支持并发写入,第二个进程会立即收到错误。要支持并发写入,需要使用PostgreSQL作为目录后端,因为PostgreSQL处理行级锁定,允许多个DuckDB进程同时写入。

DuckLake如何通过分区组织S3中的数据?

DuckLake使用Hive分区约定,将Parquet文件组织成目录层级,路径格式为{DATA_PATH}/{schema}/{table}/{partition_key=value}/.../{file}.parquet。例如,按tenant_id和event_date分区,会生成类似s3://ducklake-data/main/sensors/tenant_id=tenant_a/event_date=2025-01-01/ducklake-<uuid>.parquet的路径。

DuckLake如何实现分区剪枝?能举例说明吗?

DuckLake通过查询目录中的列统计信息来执行分区剪枝。当查询包含分区列的过滤条件时,DuckLake会先查询目录,确定哪些Parquet文件可能匹配,然后跳过不匹配的文件,无需访问对象存储。例如,在一个按tenant_id和event_date分区的表中,查询WHERE tenant_id = 'tenant_a' AND event_date = '2025-01-03'只读取1个文件,而全表扫描需要读取30个文件。

DuckLake的谓词下推在视图和连接中是否有效?

是的,DuckLake的谓词下推在视图和连接中有效。即使查询通过多层视图,过滤器也会被下推到基表并触发分区剪枝。例如,在三个嵌套视图上查询,日期过滤器会穿透所有视图,只读取3个文件而不是30个。在连接中,谓词会被推送到连接的每一侧,独立进行剪枝。

🏷️

标签

➡️

继续阅读