不含数据的 DuckDB 数据库:远程 Parquet 视图目录
DataHot 速览
文章介绍一种用法:DuckDB 数据库文件可以不存数据,只保存指向外部文件的视图定义,例如对象存储上的 Parquet。这样的文件无论描述的数据多大都保持几百 KB,附加后即可看到一组可查询的命名关系。作者引用 Nikolas Goebel 的 “DuckDB doesn't need data”,认为数据库已把数据存储与读取分离,DuckDB 可成为数据云的浏览器。文中以荷兰铁路开放数据集为例,演示安装 httpfs 并创建指向远程数据的视图,构建可分享的目录或语义层。
为什么值得关注:DuckDB 以零数据副本、远程 Parquet 视图和可分享目录的方式承载语义层,对湖仓、数据目录与轻量分析架构有直接参考价值。
译文
AI 逐段翻译DuckDB 数据库文件不必包含任何数据。它可以什么都不存储,只保存 视图 定义,这些定义指向存储在别处的文件,例如对象存储上的 Parquet。无论它所描述的数据有多大,该文件都保持在几百千字节,任何附加它的人都会看到一组可供查询的命名关系。
Nikolas Goebel 在 DuckDB doesn't need data 中描述了这一想法,该文认为现代数据库已将数据的存储位置与读取方式分离,以至于大多数数据库不再需要自己保存任何数据。从这个角度来看,DuckDB 成为数据云的浏览器,你通过 URL 而不是自己磁盘上的副本来访问数据集。
在这篇博客文章中,我们将这一想法变成一个小型、可共享的目录。
想法
DuckDB 表将行存储在数据库文件内部。视图只存储查询文本。当你读取视图时,DuckDB 运行该查询并返回当前结果。如果查询读取远程 Parquet 文件,数据位于对象存储中,视图只保存如何读取它的指令。
完全由此类视图构成的数据库文件不包含自己的任何行。它是一个目录:一组命名的、可直接查询的关系,建立在保持原始位置和格式的数据之上。你可以将该目录发布为数据湖之上的精选语义层,其中视图定义编码了使用者应使用的连接、过滤器和列名,而无需复制或移动底层数据。
构建视图目录
这些示例使用 DuckDB 的公共列车服务数据集,该数据集为荷兰铁路列车停靠的每一站记录一行。数据来自 Rijden de Treinen (列车在运行吗?) 应用程序发布的开放数据集,你可以直接从其 URL 读取它。
首先,安装 httpfs 扩展,以便 DuckDB 可以通过网络读取。这只需运行一次。
INSTALL httpfs;现在创建一个持久数据库文件,并在远程数据之上定义一个视图。使用完全限定的位置,远程 URL 或绝对路径,以便视图无论客户端的工作目录如何都能解析。
ATTACH'rail_catalog.duckdb'ASrail;USErail;CREATEVIEWservicesASSELECT*FROM'https://blobs.duckdb.org/train_services.parquet';该目录现在公开一个 services 关系,每次查询时都会从远程文件读取。
SELECTdeparture_time,station_name,typeFROMrail.servicesLIMIT3;| departure_time | station_name | type |
|---|---|---|
| 2023-05-15 00:00:00 | Rotterdam Centraal | Intercity |
| 2023-05-15 00:13:00 | Delft | Intercity |
| 2023-05-15 00:29:00 | Den Haag HS | Intercity |
因为该文件只存储视图定义,所以无论被引用的数据集增长到多大,它都保持很小。train_services.parquet 文件包含 380,959 行,而读取它的目录只有几百千字节。
托管目录
将 rail_catalog.duckdb 上传到 DuckDB 可以通过网络读取的任何位置,例如 s3:// 存储桶或 https:// 端点。已经存放 Parquet 文件的同一存储桶是放置它的便利位置。
共享目录就意味着共享一个 URL,而不是一长串单个文件路径。接收者不需要知道每个视图背后有多少文件或它们存储在哪里,只需要目录的地址。
以只读方式附加目录
使用者以 只读 方式附加目录,并像查询本地表一样查询视图。每次查询都会从被引用的文件中读取当前数据,因此结果反映当时对象存储中的内容。
ATTACH'https://example.com/rail_catalog.duckdb'ASrail(READ_ONLY);SELECTstation_name,count(*)AScallsFROMrail.servicesGROUPBYstation_nameORDERBYcallsDESCLIMIT3;| station_name | calls |
|---|---|
| Utrecht Centraal | 7663 |
| Amsterdam Centraal | 7591 |
| Zwolle | 5013 |
使用者需要 httpfs 扩展,对于 s3:// 源,还需要授予访问底层数据的 凭据。DuckDB 只读取查询所需的列和行组,因此对大型远程数据集的过滤或聚合查询不会下载整个数据集。
应对数据布局的变化
因为查询读取的是视图而不是直接读取文件,所以发布者可以更改文件而不更改查询。使用者更改前后运行相同的查询。
假设单个 Parquet 文件变得太大,发布者将其拆分为每年一个文件。将视图更新为读取文件目录只需在目录中更改一行,使用者不会察觉。
CREATEORREPLACEVIEWservicesASSELECT*FROMread_parquet('s3://my-bucket/rail/services/year=*/*.parquet');视图还可以在文件变化时保持模式稳定。如果后续一批文件将 station_name 重命名为 station,视图可以将新列映射回使用者已经使用的名称。
CREATEORREPLACEVIEWservicesASSELECT*EXCLUDE(station),stationASstation_nameFROMread_parquet('s3://my-bucket/rail/services/*.parquet');如果你重新分区文件或将它们移动到新的存储桶,你只需更新目录。任何查询它的人都会继续运行与之前相同的查询。
何时使用
当你希望共享数据集而不向接收者发送一长串单个文件路径时,以及当数据已经采用 DuckDB 能够很好读取的格式(例如 Parquet)时,使用此模式。读者获得一组可查询的命名关系,发布者可以更改文件布局而不破坏那些查询。
注意以下限制:
- 使用者必须能够访问目录文件及其视图引用的每个数据源。
- 通过 HTTPS 和 S3 API 的连接是 只读 的,因此无法就地修改目录。要更改视图,请编辑本地副本并再次上传。
- 查询性能取决于 网络和被引用文件的布局。查询远程文件 的技巧同样适用。
结论
一个只保存视图的 DuckDB 文件是一个小型目录,你可以将其作为单个 URL 共享。数据保留在原始位置,发布者可以更改其存储方式,而读者获得一组稳定的关系可供查询。
完整参考请参阅 Share a DuckDB Database That Only Stores Views 指南。
这篇内容对你有用吗?
反馈只用于改善内容筛选,不等同于收藏