Apache Arrow、Parquet、Flight及其生态系统是OLAP的变革者
作者:Paul Dix / 用例,开发者
2020年4月16日
导航到
Apache Arrow,一个内存列式数据格式的规范及其相关项目:用于压缩磁盘数据的Parquet、用于高效RPC的Flight以及其他用于内存查询处理的项目可能会塑造OLAP和数据仓库系统的未来。这主要是由项目之间互操作性的承诺以及在大数据系统中推拉数据时的性能大幅提升所驱动。以S3等对象存储作为公共数据湖,OLAP项目需要一个公共数据API,Parquet就是其代表。对于数据科学和查询工作负载,它们需要一个公共RPC,该RPC针对从大数据系统中提取数百万条记录进行了优化,以便执行更复杂的分析和机器学习任务。
在这篇文章中,我将涵盖这些领域的每个方面以及为什么我认为Apache Arrow项目系列代表了围绕当前和未来大数据、OLAP和数据仓库项目协作和创新的通用API。最后,我将提出一些关于这些项目目前所处位置以及未来可能走向的看法。
Apache Arrow
Apache Arrow是一个内存列式数据格式。它旨在利用现代CPU架构(如SIMD)以实现列式数据的快速性能。它非常适合矢量化分析查询。Arrow规范为列式和嵌套数据提供了一个标准内存布局,可以在进程和查询处理库之间共享。它是处理内存(或MMAP’d)数据的底层构建块,将所有内容连接在一起。它旨在实现零拷贝语义,以尽可能快和高效地移动数据。
持久性、大批量数据和Parquet
数据是API。我确信在我之前有人说过这句话,但我在2010年谈论面向服务的架构和构建通过像RabbitMQ和Kafka这样的消息总线进行交互的松散耦合系统时自己说过。我的意思是在当时,通过消息队列传递的数据作为通过这些队列相互集成的服务的API。并且像API一样,这些数据需要有一个共同的序列化格式,其模式应该是版本化的。更广泛地说,我试图强调数据交换的重要性,这对于数据处理和分析系统来说与对于服务一样重要。
历史上,OLAP和数据仓库系统共享的主要接口点是某种形式的SQL(通常有限)作为查询接口和ODBC作为RPC API。SQL在查询方面工作得足够好,但ODBC在系统内外批量移动数据方面效率并不高(更新:turbodbc的工作似乎有所改进[链接],但仍不如本地Arrow的效率)。批量数据移动迅速成为进行机器学习、数据科学或进行SQL能力之外的更复杂事物所必需的。早期,批量数据移动对互操作性不是一个大问题,因为每个OLAP系统都必须关心自己的存储,并且通常作为一个孤岛运行。然而,随着像Hadoop这样的数据湖变得普遍,OLAP系统需要一种共同的格式来使用其他后端存储系统。
由于像对象存储和S3类似API这样的共享数据湖的兴起,对通用文件持久化格式的需求得到了加速。系统需要一种从对象存储中批量推送和拉取数据的方法。虽然CSV和JSON文件对用户友好,但它们在大小和吞吐量方面效率都很低。Apache Parquet作为Cloudera的产物,成为压缩列式和分层数据的一个通用文件格式。Parquet现在已成为在大数据系统(如Hive、Drill、Impala、Presto、Spark、Kudu、Redshift、BigQuery、Snowflake、Clickhouse等)中作为输入(以及经常作为输出)接受的格式。
近年来,Parquet的开发带来了更多实现和可用性,对以其他语言读取和写入文件的支持越来越多。目前,看起来C++、Python(与C++实现绑定)和Java在Arrow项目中都有第一级支持读取和写入Parquet文件。R看起来对读取有很好的支持,但我对写入方面不太确定(更新:R的写入支持也很好,因为它使用相同的C++库)。Ruby看起来也有绑定。有一个部分Rust实现用于读取Parquet文件。在顶级语言中添加一级支持将是巩固Parquet作为压缩列式数据持久化格式的普遍性的必要步骤。然而,Parquet的性能提升以及与其他系统互操作的承诺将激励开发者完成这项工作。Wes McKinney在2019年10月的一篇关于Parquet性能的帖子是关于这些提升的好回顾。
RPC和将数据移动到需要的地方
现在我们有了持久化压缩数据的通用格式,我们需要一个更高效的格式来传输这些数据,而不需要序列化和反序列化的开销。这是Apache Arrow Flight,一个快速数据传输框架的目标。它定义了一个gRPC API,将Arrow Array数据包裹在FlatBuffers中,以描述元数据,如模式、字典和记录批次之间的间隔(匹配相同模式和行数的Arrow Array数据集合)。我无法比Wes McKinney的帖子描述得更好,所以我推荐您阅读链接中的内容,如果您需要了解更多信息。
Flight有几个令人兴奋的特点。首先,它非常快。为了尝试新功能,我用Rust编写了一个简单的测试程序,向Python消费者发送飞行数据。我生成的测试数据的模式如下
let fields: Vec<Field> = vec![
Field::new("host", DataType::Utf8, false),
Field::new("region", DataType::Utf8, false),
Field::new("usage_user", DataType::Float64, true),
Field::new("usage_system", DataType::Float64, true),
Field::new("timestamp", DataType::Int64, false),
];
我们有一个包含五个字段的数据表:两个字符串,两个float64和一个int64。我让程序生成100个记录批次,每个批次包含100,000行,总共1,000万条记录。每个记录批次代表一个单独的主机上的测试数据,来自两个地区之一。我将这些数据连接到了一个简单的Flight API,以获取这些数据。以下是一个用Rust编写的Flight服务器示例。然后,我在Jupyter Notebook中编写了一个Python脚本来连接到这个服务器(本地主机)并调用API。代码非常简单
cn = flight.connect(("localhost", 50051))
data = cn.do_get(flight.Ticket(""))
df = data.read_pandas()
在Python的3行代码中(不包括导入),我们已经连接到Flight服务器,执行了get操作,并将结果读入Pandas DataFrame。我传递给flight.Ticket的空字符串是您可以放置有效负载(如JSON blob)的地方,以便您的API服务器可以解析请求并执行查询或其他操作。对于这次测试,我只想返回内存中的数据。
在我本地主机的get请求中,多次运行的时间大约为311ms +/- 50ms。通过网络传输的数据大小约为452MB。将这些数据读入Pandas DataFrame大约需要1s +/- 100ms。因此,现在我们在不到1.5秒内就准备好了1,000万条记录,可以用于我们最喜欢的数据科学工具。鉴于我的示例数据中有许多重复的字符串,我们可能会通过在Arrow中使用Dictionary类型和在Flight中使用DictionaryBatch来做得更好。Rust对这方面的支持目前还不足,但我将在接下来的几周内提交自己的修改。
Flight的第二个优点是与其他系统的潜在集成。它可以作为跨许多语言的标准化RPC,有助于数据科学和分布式查询执行等任务。虽然Arrow是一个Apache项目,具有开放治理,但我的看法是它主要是由Wes McKinney及其在Ursa Labs的团队的努力推动的,Ursa Labs是一个非营利组织,旨在推动Arrow的发展。是的,还有其他人参与和贡献,如Dremio和Cloudera的成员,等等,但Ursa的努力看起来是推动力。值得注意的是,R数据科学界著名的Hadley Wickham是顾问。
我提到Wes和Hadley的参与,因为这意味着您有两个最大的开源数据科学贡献者正在为Arrow及其项目做出贡献。这意味着Python(Wes是Pandas的创建者)和R(Hadley是Tidyverse的创建者)都将为Arrow和Flight提供第一流的支持,并将随着它们的发展而发展。这使得支持Arrow和Flight的服务成为数据科学家们的显而易见的选择。
快速、内存查询
在库中添加查询功能的工作还处于初级阶段,尚未完全开发。2019年3月,Wes和其他Arrow贡献者编写了一些基于Arrow的列式查询执行引擎的设计要求。这是在Dremio将其项目Gandiva捐赠给Arrow项目之后。Gandiva是一个基于LLVM的分析表达式编译器,用于Arrow。
值得注意的是,这两个项目都是用C++编写的。这意味着,就像Arrow一样,它们可以被嵌入您选择的任何高级语言中。去年,还有由Andy Grove贡献的名为DataFusion的Rust本地查询引擎,用于Arrow。
构建Arrow本地查询引擎的努力还处于初级阶段,但我认为它们闭合了Arrow项目集提供的功能集。您不仅可以在服务和持久化之间发送和接收大量数据集,还能以出色的性能使用和查询它们,并在您选择的任何语言中完成。
这些查询引擎还代表了一个领域,更多OLAP和数据仓库供应商可能会在此进行贡献和协作,共享努力。在规模上运行数据处理系统非常困难,需要大量的代码,而这部分代码是查询引擎之外的。这意味着供应商可以在不放弃其核心知识产权的情况下,为开源查询引擎做出贡献。
目前的情况以及它意味着什么
Parquet格式似乎是这个工具套件中最成熟的部分。然而,对于许多语言来说,读取和写入Parquet文件的支持仍处于初级阶段。
Arrow内存中的列式格式虽然已经存在一段时间了,但仍在快速发展。他们最近推出的Arrow C数据接口应该会使其他语言的库作者更容易与Arrow过程库和项目进行接口。
Flight协议仍在发展中,各种语言的支持仍然处于初级阶段(Flight去年10月推出)。查询引擎甚至更早。
尽管如此,我对这些项目可能达到的境界感到非常兴奋。它们共同代表了一套可用于构建数据科学、分析和大规模数据处理组件的工具。它们也是可以用来构建新型数据仓库和分布式OLAP引擎的原始工具。
在过去15年中,开源大数据生态系统一直由Java编写的工具主导。使用C/C++和Rust编写的库和原始工具作为基础层,为全新的工具生态系统打开了大门。我打赌Rust将成为构建服务以在网络和分布式服务中使Arrow及其部分可访问的语言选择。OLAP不仅关于大规模分析查询,还包括以毫秒级的响应速度完成的近实时查询。
开发工具和技术是一场持续的颠覆游戏。使用S3等对象存储作为数据湖,在短时间内彻底颠覆了Hadoop生态系统。容器化和Kubernetes一夜之间完全颠覆了编排和部署生态系统。随着Arrow及其相关项目的成熟,数据仓库/大数据生态系统可能即将迎来另一场颠覆。这将由对提高互操作性的渴望和一套可以嵌入任何地方的全新工具驱动。再加上这些系统在Kubernetes等环境中运行的新设计要求,我们可能会在接下来的五年内看到该领域的一组全新的领导者。