我的 DuckDB ORC 扩展进社区了
三周前我开始写一个 DuckDB 扩展,让它能读 Apache ORC 文件。昨天它被合并进了 DuckDB 社区扩展仓库。
这意味着从今天起,任何 DuckDB 用户都可以这样读 ORC:
INSTALL orc FROM community; LOAD orc; SELECT * FROM read_orc('data.orc');不用装 Java,不用配 Hadoop,一行 SQL 搞定。
● ● ●
为什么做这个
DuckDB 这几年在分析场景里增长很快,但它一直不支持 ORC 格式。ORC 是 Hive/Spark/Trino 生态的标准列存格式,大量企业的历史数据都是 ORC。
DuckDB 本身有一个 C++ 写的 ORC reader,但只作为内部依赖,没有暴露给用户,也不在社区扩展里。
我做的这个扩展用了 datafusion-contrib 的 orc-rust,纯 Rust 实现,零 JVM 依赖。所有 ORC 数据类型和压缩编码都支持——Zlib、Snappy、LZO、LZ4、ZSTD,全部覆盖。
● ● ●
技术细节
底层链路是:
read_orc('data.orc') → orc-rust (Rust ORC reader, Arrow 输出) → DuckDB C API (arrow→DuckDB 类型映射)几个关键设计选择:
Projection pushdown。列裁剪通过 DuckDB 的 optimizer 下推到扫描层,SELECT col1 FROM big_file.orc 只读你需要的列。
Stripe 级并行。ORC 文件的 stripes(类似 Parquet 的 row groups)可以并行读取。多 stripe 文件用 std::thread + mpsc channel 流式处理,实测 50M 行 / 2 stripes 从 4s 降到 2s。
Multi-batch 流式。不管文件多大,都按 2048 行一批一批往外送,不会 OOM。
● ● ●
已知局限
目前有两个限制值得说明:
- 01Filter pushdown 做不了
。DuckDB 的 C extension API 没有暴露 duckdb_table_function_supports_filter_pushdown,需要上游改。DuckDB 仍然会在 scan 之后做过滤,只是不能下推到文件层。
- 01单文件性能不如 Parquet
。DuckDB 的 Parquet reader 原生支持 row group 并行。ORC 这边虽然做了 stripe 并行,但大文件仍然比 Parquet 慢 5 倍左右。如果追求性能,建议先把 ORC 转 Parquet。
● ● ●
怎么装
DuckDB v1.5.5+:
INSTALL orc FROM community; LOAD orc; -- 读单个文件 SELECT * FROM read_orc('users.orc') LIMIT 10; -- 读目录 SELECT count(*) FROM read_orc('/data/events/*.orc'); -- 和 Parquet/CSV 混查 SELECT o.user_id, p.name FROM read_orc('orders.orc') o JOIN read_parquet('products.parquet') p ON o.product_id = p.id;● ● ●
后续计划
PR #2239 合并后,CI 会自动构建 Linux/macOS/Windows 三平台的二进制包,社区的 extension index 下次刷新后就能直接用 INSTALL orc FROM community 安装。
写功能也在考虑中。orc-rust 上游已经有了 writer 和压缩支持(SNAPPY/ZLIB/ZSTD),等 PR #82 合并后可以加上 COPY ... TO 'xxx.orc'。
代码在 alitrack/duckdb_orc,MIT 协议。试试看,跑通了告诉我。