DuckDB Avro 插件来了:全新支持 Avro 文件读取
作者:Hannes Mühleisen
原文: https://duckdb.org/2024/12/09/duckdb-avro-extension.html
摘要:DuckDB 现在通过 avro 社区扩展提供了对 Avro 文件的读取支持。
什么是 Apache™ Avro™ 格式?
Avro[1] 是一种二进制数据记录格式,最早由 Doug Cutting[2] 在 2009 年为 Apache Hadoop 项目开发而成。这个名字的来源相当有趣——它借用了一个已经停产的 英国航空制造公司 Avro[3] 的名字,该公司曾在二战期间制造了超过 7,000 架 Avro Lancaster 重型轰炸机[4]。
Avro 的诞生是为了应对复杂多维数据结构(例如表格,可能还包含嵌套类型)向一维存储结构(如纯字节序列文件)转换时遇到的难题。这一问题也被称为“维度缩减”。在这一过程中,最核心的问题之一是选择数据存储的布局方式:是按列存储还是按行存储。Avro 采用的是按行存储(Row-major)的方式,与它更知名的“亲戚” Apache™ Parquet™[5] 的按列存储(Columnar)布局截然不同。
那么为什么要选择按行存储呢?一个典型的应用场景是,当需要向一个文件中追加少量新行时,列式存储的操作会很复杂且低效。这是因为 Parquet 文件的元数据通常存储在文件末尾。而 Avro 的按行存储布局,元数据位于文件的开头,因此可以更方便地追加新行。
Avro 编码的数据可以以多种形式出现,例如 RPC 消息[6],也可以保存为文件。在本文中,我们将重点介绍 Avro 文件,因为它们适合长期保存和使用。
Avro 文件结构解析
头块(Header Block)
每个 Avro 文件的开头是一个头块,它的格式相对简单:
1. 头块以一个固定的 魔术字节[7] Obj1开始。2. 随后是一个元数据“映射”,即包含字符串和字节数组键值对的列表。这个映射至少需要包含一个 avro.schema键,它以 JSON 格式保存了文件的模式定义(Schema)。以下是一个示例:
{
"namespace":"example.avro",
"type":"record",
"name":"User",
"fields":[
{"name":"name","type":"string"},
{"name":"favorite_number","type":["int","null"]},
{"name":"favorite_color","type":["string","null"]}
]
}在 Avro 中,模式定义了数据记录的结构。记录可以包含标量类型的数据(例如 int、double、string),也可以包含更复杂的类型(例如嵌套的记录、联合类型和列表)。有趣的是,尽管 Avro 本身是一种用于定义记录结构的二进制格式,但它却选择了 JSON 格式来描述自己的模式,这也算是 Avro 的“独特之处”。
数据块(Data Blocks)
头块的最后包含一段 16 字节的随机数据,称为“同步标记”(Sync Marker)。
紧接着头块之后,是文件的核心内容——数据块。每个数据块包含以下信息:
1. 记录数:表示该块中包含了多少条数据。 2. 大小:表示数据块的字节大小。 3. 记录字节数组:包含实际的数据记录。这部分数据可以选择使用 deflate(gzip)进行压缩。
在解析 Avro 数据时,需要使用模式(Schema)来解码这些数据。数据块的末尾也包含一个同步标记,用于校验数据完整性,确保文件中没有错误或多余数据。
DuckDB 的 avro 社区扩展
DuckDB 通过全新的 avro 社区扩展,让用户可以直接读取 Avro 文件并将其内容呈现为 DuckDB 表。值得一提的是,这个扩展仅支持读取功能,并未提供写入 Avro 文件的能力。这一设计是有意为之,希望减少 Avro 文件的使用量。
如何安装和加载扩展?
安装和加载非常简单,只需在 DuckDB 中执行以下命令:
INSTALL avro FROM community;
LOAD avro;目前,由于依赖问题,此扩展尚未支持 Wasm 平台。
read_avro 函数使用说明
此扩展提供了一个核心函数 read_avro,用于将 Avro 文件内容加载为 DuckDB 表。例如:
FROM read_avro('some_example_file.avro');函数不仅支持本地文件,还可以直接从 HTTP 或 S3 等远程存储中读取 Avro 文件,例如:
FROM read_avro('http://blobs.duckdb.org/data/userdata1.avro');
FROM read_avro('s3://my-example-bucket/some_example_file.avro');此外,您可以一次性读取多个文件,例如使用通配符或文件列表:
FROM read_avro('some_example_file_*.avro');
FROM read_avro(['file1.avro', 'file2.avro']);如果文件名中包含重要信息(例如时间戳),可以通过 filename = true 参数添加一个额外的列,显示文件名:
FROM read_avro('some_example_file_*.avro', filename =true);Avro 模式与 DuckDB 模式的映射
该扩展会自动将 Avro 模式转换为 DuckDB 的模式。对于 Avro 的联合类型(例如 ["int", "null"]),扩展会将其简化为 DuckDB 的基本类型(例如 INTEGER),并允许值为 NULL。另外,Avro 中的嵌套记录类型(类似 STRUCT)会被“扁平化”处理,方便用户直接访问顶层字段。
当前限制和未来计划
1. 无并行读取支持:当前扩展不支持对单个大文件或文件列表进行并行读取,后续版本计划支持此功能。 2. 无投影或过滤下推支持:尚未支持过滤条件直接传递到数据读取层。 3. 平台限制:目前不支持 Wasm 和 Windows-MinGW 构建,未来计划解决。 4. 递归类型定义不支持:DuckDB 无法解析 Avro 的递归数据结构,这一限制可能不会改变。 5. 独立模式文件不支持:扩展仅支持嵌入模式的 Avro 文件,不支持单独提供模式定义。
总结
DuckDB 的 avro 社区扩展为数据分析提供了新的工具,使用户可以直接将 Avro 文件加载为 DuckDB 表,无需额外的转换步骤。如果您有大量 Avro 文件,赶快试试吧!遇到任何问题,欢迎在 GitHub[8] 提交反馈。
引用链接
[1] Avro:https://avro.apache.org[2]Doug Cutting:https://en.wikipedia.org/wiki/Doug_Cutting[3]英国航空制造公司 Avro:https://en.wikipedia.org/wiki/Avro[4]Avro Lancaster 重型轰炸机:https://en.wikipedia.org/wiki/Avro_Lancaster[5]Apache™ Parquet™:https://parquet.apache.org[6]RPC 消息:https://en.wikipedia.org/wiki/Remote_procedure_call[7]魔术字节:https://en.wikipedia.org/wiki/List_of_file_signatures[8]GitHub: https://github.com/hannes/duckdb_avro/issues