我给 DuckDB 做了个 ORC 读取器
DuckDB 能读 Parquet、CSV、JSON、Iceberg、Delta……唯独没有 ORC。
Apache ORC 是 Hive 生态的标准列存格式。如果你跑 Spark 或者数仓导出的是 .orc 文件,到了 DuckDB 这边就是死路——没有官方扩展,社区也没有能用的。
我写了一个。alitrack/duckdb_orc,纯 Rust,187 行核心代码。
仓库:github.com/alitrack/duckdb_orc
● ● ●
怎么用
LOAD 'orc.duckdb_extension';
-- 读单个文件
SELECT * FROM read_orc('sales.orc');
-- glob 多文件
SELECT * FROM read_orc('data/2024/*.orc');
-- 跟普通表一样过滤聚合
SELECT region, SUM(amount)
FROM read_orc('sales.orc')
WHERE year = 2024
GROUP BY region;
跟 read_parquet 一样的体验。支持所有 ORC 数据类型(struct/list/map)、所有压缩编码(Zlib/Snappy/LZO/LZ4/ZSTD),大文件流式读取,不会 OOM。
● ● ●
为什么是 Rust
之前有人用 C 试过——quackmagic/duckdb-extension-orc,依赖 Apache ORC C 库,整个文件加载到内存,没有流式读取,没有测试,0 star。
Rust 生态里有个 orc-rust,DataFusion 团队维护的纯 Rust ORC 读取器,Arrow 输出,直接对接到 DuckDB 的 Arrow C Data Interface。类型全、编码全、压缩全,而且 DataFusion 已经在生产环境验证过了。
底层引擎选对了,上面就是胶水代码的事。
仓库:github.com/datafusion-contrib/orc-rust
● ● ●
架构
read_orc('data/*.orc')
│
▼
glob 展开 → 文件列表 → schema 校验
│
▼
orc-rust ArrowReader (batch_size=2048)
│
▼
RecordBatch → DuckDB DataChunk (零拷贝)
整个过程没有 C++ 编译链,cargo build 一把梭。
● ● ●
做到了什么,卡在哪里
| 能力 | 状态 |
|---|---|
| 全 ORC 类型、编码、压缩 | ✅ |
| 流式读取(大文件) | ✅ |
| glob 多文件 | ✅ |
| 列裁剪(projection pushdown) | ✅ |
| 谓词下推(filter pushdown) | ❌ |
| 写入 | ❌ |
列裁剪是 DuckDB 自动做的——SELECT name FROM read_orc(...) 只读 name 列。这部分开箱即用。
谓词下推卡在了 DuckDB 上游。DuckDB 的 C 扩展 API 只暴露了 projection pushdown,没有对应的 filter pushdown 接口。所有 Rust 扩展——包括社区里已经发布的 rusty-sheet——都面临同一个天花板。
不过 DuckDB 仍然会在扫描完成后应用 WHERE 过滤,数据不会漏。只是不能利用 ORC stripe 级别的 min/max 统计跳过不相关的数据块。
● ● ●
跨平台 CI
用 DuckDB 官方的 extension-ci-tools 搭了多平台构建流水线:
- linux_amd64 ✅
- linux_arm64 ✅
- osx_amd64 ✅
- osx_arm64 ✅
打 tag 自动发布 GitHub Release,所有平台的 .duckdb_extension 二进制文件都可以直接下载。
Release:github.com/alitrack/duckdb_orc/releases
● ● ●
下一步
三个方向:
- 01给 DuckDB 上游提 PR,加 filter pushdown 的 C API——这是唯一能打通谓词下推的路径
- 02提交到社区扩展仓库(duckdb/community-extensions),让用户
INSTALL orc FROM community就能装 - 03写支持——orc-rust 只读,短期内不太可能
做完第二步,DuckDB + ORC 这件事就闭环了。