用 apache.orc 库读写 orc 文件,它基于 .net standard 2.0+,依赖本地 liborc 动态库,需按平台正确部署 native 库并手动处理 schema 类型映射、内存管理和 stripe/压缩配置。

用什么库读写 ORC 文件(C# 没原生支持)
C# 标准库不支持 ORC,得靠第三方。目前唯一靠谱的是 Apache.ORC(官方 C# 绑定),基于 .NET Standard 2.0+,由 Apache ORC 项目维护。别被名字误导——它不是纯托管实现,底层依赖本地 liborc 动态库,所以跨平台部署时得同步放对版本的 .so / .dylib / .dll。
常见错误现象:DllNotFoundException: liborc 或 Unable to load shared library 'orc'。这是因为没把对应平台的 native 库放进输出目录,或路径没加进 PATH(Windows)/ LD_LIBRARY_PATH(Linux)。
- Windows:NuGet 安装
Apache.ORC后,检查runtime/win-x64/native/orc.dll是否被复制到bin/Debug - Linux:手动下载对应架构的
liborc.so(如从 ORC 发布页),确保LD_LIBRARY_PATH包含其所在目录 - macOS:同理,用
liborc.dylib,注意 SIP 可能阻止加载未签名 dylib
读 ORC 文件:Schema 和类型映射是最大坑
ORC 的 schema 是强类型的,但 C# 里没有直接对应的 decimal(38,10) 或 timestamp with timezone 类型。Apache.ORC 把它们降级成 string 或 long,你得自己解析。
比如读一个 decimal 列,实际拿到的是 orc::ColumnVector 中的 long 值,代表“无缩放整数”,必须结合 schema 里的 scale 手动除以 Math.Pow(10, scale);timestamp 则是纳秒级 long,得转成 DateTimeOffset 再处理时区。
- 务必用
Reader.Options().IncludeSchema(true)显式开启 schema 读取,否则GetColumnVector返回空 - 列名大小写敏感,ORC 文件里存的是原始定义名,C# 代码里写错大小写会静默返回 null 向量
- 大文件别用
ReadAllBatches()一次性加载——内存爆掉比读取慢更常见
写 ORC 文件:压缩、stripe size 和类型对齐很关键
写 ORC 不是“把数据塞进去就完事”。默认 stripe size 是 64MB,小文件写入大量小 stripe 会导致元数据膨胀、读性能下降;压缩算法选错(比如用 ZLIB 写高频数值列)反而比 SNAPPY 更慢且更大。
更重要的是类型对齐:C# 的 int 写进 ORC 的 int 列没问题,但 long 写进 int 列会抛 OrcFormatException(不是隐式转换,是严格校验)。schema 必须和数据类型完全匹配。
- 写前用
WriterOptions().StripeSize(256 * 1024 * 1024)调大 stripe(尤其日志类宽表) - 数值密集场景优先用
CompressionKind.SNAPPY,文本多用ZSTD(需 ORC v1.7+ 和对应 native 库) - schema 字符串必须用
OrcType.Struct构建,不能靠反射自动推导——推导结果常漏掉 nullable 标记或精度信息
大数据场景下,别直接用 ORC API 做流式处理
Apache.ORC 的 C# API 是批式设计,没有 IAsyncEnumerable<row></row> 或流式 writer。想边读边处理 TB 级文件?硬扛 Batch 对象会触发 GC 风暴,尤其在容器内存受限环境。
真实做法是分层:用 Reader 按 stripe 或 row group 分块读出 ColumnVector 数组 → 转成 Span<t></t> 或 Memory<t></t> 做零拷贝计算 → 结果再喂给下游(比如 Spark DataFrame 或 Parquet writer)。中间绝不 new 大对象。
- 避免
ToString()所有字符串列——ORC 内部是字典编码,ToString()强制解码,性能跌 5x+ - 列裁剪必须用
ReaderOptions().IncludeColumnNames(...),别靠事后Where过滤 - 遇到
OutOfMemoryException先查GC.GetTotalMemory,90% 是因为 vector 缓冲没及时Dispose()
ORC 的复杂性不在语法,而在 schema 精度、native 依赖、内存生命周期这三处。漏掉任何一环,都会在数据量上来后突然崩掉,而且错误提示往往不指向根因。










