alitrack

我的 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。

● ● ●

已知局限

目前有两个限制值得说明:

  1. 01Filter pushdown 做不了
    。DuckDB 的 C extension API 没有暴露 duckdb_table_function_supports_filter_pushdown,需要上游改。DuckDB 仍然会在 scan 之后做过滤,只是不能下推到文件层。
  1. 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 协议。试试看,跑通了告诉我。