37 个版本

0.17.2 2023年4月13日
0.17.1 2022年12月12日
0.17.0 2022年11月30日
0.14.2 2022年7月26日
0.1.0 2021年7月20日

#118解析器实现

Download history 6087/week @ 2024-03-14 5293/week @ 2024-03-21 6243/week @ 2024-03-28 6951/week @ 2024-04-04 6503/week @ 2024-04-11 5849/week @ 2024-04-18 4902/week @ 2024-04-25 5877/week @ 2024-05-02 6262/week @ 2024-05-09 4785/week @ 2024-05-16 3921/week @ 2024-05-23 2818/week @ 2024-05-30 4327/week @ 2024-06-06 4382/week @ 2024-06-13 5814/week @ 2024-06-20 4901/week @ 2024-06-27

19,836 每月下载量
用于 31 个crate(6 个直接使用)

Apache-2.0

440KB
11K SLoC

Parquet2

这是一个以性能、并行性和安全性为重点,对官方parquet crate的重写。

查看指南了解如何使用此crate读取parquet。

parquet相比,五个主要的不同之处在于

  • 它使用#![forbid(unsafe_code)]
  • 将并行性委托给下游
  • 将读取(I/O密集型)与计算(CPU密集型)解耦
  • 它更快(读取到arrow格式时快10-20倍)
  • 支持异步读写。
  • 它已与pyarrow和(py)spark 3集成测试

总体思想是提供读取压缩的parquet页面的能力,以及将它们解压缩到喜欢的内存格式的工具包。

这允许此crate的迭代器执行最小的CPU工作,从而最大化吞吐量。消费者是否想要通过牺牲内存使用来利用并行性(例如,在线程中解压缩和反序列化页面)由他们自己决定。

此crate不能直接用于读取parquet(除了元数据)。要从parquet读取数据,请查看arrow2

已实现的功能

  • 读取字典页面
  • 读取和写入V1页面
  • 读取和写入V2页面
  • 压缩和解压缩(全部)

尚未实现的功能

Parquet格式针对不同的物理类型有多种编码策略。此crate目前几乎支持从所有这些类型中读取,并支持将它们编码到子集。它们是

支持的解码

Delta编码仍然是实验性的,因为我无法从spark生成使用它们的编码的大页面,这阻碍了鲁棒性集成测试。

编码

组织

  • read:读取元数据和页面
  • write:写入元数据和页面
  • encoding:不同parquet编码的编码器和解码器
  • page:页面声明
  • metadata:parquet文件元数据(例如FileMetaData
  • schema:类型元数据声明(例如ConvertedType
  • types.rs:物理类型声明(即如何在内存中表示)。
  • statistics:反序列化表示的parquet页面
  • compression:压缩和解压缩器压缩(例如Gzip)
  • error:错误声明

运行集成测试

有针对由pyarrow生成的parquet文件的集成测试。要运行,您需要先运行以下命令:

python3 -m venv venv
venv/bin/pip install pip --upgrade
venv/bin/pip install pyarrow==7
venv/bin/python tests/write_pyarrow.py
cargo test

。这只需要一次(每次更改tests/write_pyarrow.py)。

如何实现页面读取器

用于消耗parquet页面的内存中格式强烈影响了页面应该如何反序列化。因此,此crate不承诺特定的内存格式。消费者负责将页面转换为他们的目标内存格式。

此git仓库包含一个序列化到简单内存格式的示例,在integration中使用,用于验证与其他实现一起的集成。

还有对arrow格式的实现,请在此处查看这里

更高的并行度

通常,将页面转换为内存是昂贵的,因此考虑如何将工作分配到线程。例如:

let handles = vec![];
for column in columns {
    let column_meta = metadata.row_groups[row_group].column(column);
    let compressed_pages = get_page_iterator(column_meta, &mut file, file)?.collect()?;
    // each compressed_page has a buffer; cloning is expensive(!). We move it so that the memory
    // is released at the end of the processing.
    handles.push(thread::spawn move {
        page_iter_to_array(compressed_pages.into_iter())
    })
}
let columns_from_all_groups = handles.join_all();

这将在主线程中以尽可能快的速度读取文件,并将CPU密集型工作发送到其他线程,从而最大化IO读取(以在内存中存储多个压缩页面为代价;这里也可以选择缓冲)。

解码流

通常,以以下方式读取parquet文件:

  1. 读取元数据
  2. 查找行组和列
  3. 遍历(压缩)页面

这是IO密集型,需要解析thrift,并在文件中进行查找。

一旦压缩页面被加载到内存中,它就可以被解压缩、解码并反序列化为特定的内存格式。所有这些操作都是CPU密集型的,因此留给消费者执行,因为消费者可能希望将此工作发送到线程。

读取->压缩页面->解压缩页面->解码字节->反序列化

许可证

根据Apache License,Version 2.0或MIT许可证许可。

您可以选择。

贡献

除非您明确声明,否则任何有意提交给作品以包括在内的贡献,如Apache-2.0许可证所定义,将根据上述许可证双许可,没有额外的条款或条件。

依赖关系

~12MB
~364K SLoC