ParquetSharp是目前C#生态中稳定支持Parquet读写的主流方案,基于Apache Arrow C++实现,支持.NET 6+,推荐按列读取、显式指定压缩编码、注意DateTime和decimal类型兼容性,并避免全量加载大文件。

用 ParquetSharp 读写 Parquet 文件最可行
目前 C# 生态中稳定支持 Parquet 读写的主流方案是 ParquetSharp(基于 Apache Arrow C++ 实现的 .NET 绑定),而非已停止维护的 parquet-dotnet。它支持 .NET 6+,能处理百万行以上数据,且列式读取逻辑清晰。
安装方式:dotnet add package ParquetSharp。注意:Windows 用户需确保运行时有 vcruntime140.dll 和 msvcp140.dll(Visual C++ 2015–2022 运行库),否则加载 ParquetSharp.Native 会失败。
- 不推荐用
Microsoft.Data.Analysis直接读 Parquet——它底层依赖ParquetSharp,但封装过深,列过滤、类型映射、内存控制都不透明 - 避免手动解析二进制:Parquet 是复杂嵌套格式,含页压缩、字典编码、统计信息等,自行解析极易出错且无性能优势
-
ParquetSharp默认启用Snappy压缩;若文件是Gzip或Zstd编码,需在WriterProperties中显式指定解码器
读取时按需加载列,别全量 ReadTable()
大数据场景下,ParquetFileReader.ReadTable() 会把整个文件解压并转成内存表,极易 OOM。正确做法是只读关键列:
using var reader = new ParquetFileReader("data.parquet");
var schema = reader.Schema;
// 只读第0列(如 "user_id")和第2列(如 "amount")
var columnIndices = new[] { 0, 2 };
using var table = reader.ReadTable(columnIndices);
- 列索引必须对应 schema 中顺序,不能用字段名直接查——先调
schema.GetFieldIndex("col_name")获取索引 - 如果只需某列的统计值(如最大值),可用
ColumnChunkMetaData直接读 footer 里的Statistics,完全跳过数据解压 - 对超大文件(>1GB),建议配合
RowGroupReader分批读:每个 RowGroup 是独立压缩块,可并行处理
DateTime 和 decimal 类型容易错位
Parquet 标准对时间与高精度数字的编码有多种逻辑,C# 绑定默认行为可能和 Python/Java 写入端不一致:
-
DateTime:Parquet 常用INT96或INT64 + TIMESTAMP_MILLIS编码。ParquetSharp默认按TIMESTAMP_MICROS解析,若源文件是毫秒级,需在ReadOptions中设UseLegacyTimestamps = true -
decimal:.NET 的decimal和 Parquet 的DECIMAL(基于 INT32/INT64/INT128)不是一一映射。读取时若精度丢失,应改用BigInteger或字符串暂存,再按 scale 手动除法还原 - 枚举字段写入前务必转为
int或string,Parquet 不原生支持 .NET enum
写入性能差?关掉默认压缩或换编码
默认 Snappy 压缩在吞吐量敏感场景反而拖慢写入,尤其当 CPU 成瓶颈时:
var props = new WriterProperties(
compression: CompressionMethod.Uncompressed, // 关压缩
dictionaryPageSizeThreshold: 0 // 禁用字典编码,适合高基数列
);
using var writer = new ParquetFileWriter("out.parquet", schema, props);
- 小文件(Zstd(需额外引用
ZstdSharp并注册 NativeCodec) - 字符串列若重复值多,保留字典编码(默认开启),但要设
dictionaryPageSizeThreshold防止单页过大 - 写入前确保 schema 中每列的
LogicalType明确(如LogicalType.Decimal(precision, scale)),否则读取端可能当成普通整数
真正麻烦的是跨语言协作——Python 用 pyarrow 写的 Parquet,常带 __index_level_0__ 列或自定义元数据,C# 读时得手动跳过这些非业务字段。别指望“自动兼容”,得看 footer 里的 KeyValueMetadata 具体写了啥。










