Apache Arrow和Minio

2020-09-21 02:46:35

越来越多的企业已经开始或已经在我们几年前所做的一些工作的基础上实施了数据湖战略。如果你想花点时间回顾一下-你可以在下面和这里找到这些帖子。

在这篇文章中,我将解释一种加速Minio使用的机制。就Minio而言,没有什么变化,优化将放在我们数据的底层存储上。我们将选择一种最新的格式来全面提高敏捷性。我们将展示您的数据湖数据如何在不经历任何转换时间的情况下跨系统传输。

我相信理解这篇文章需要一些像Spark这样的应用程序是如何工作的基本概念。让我简单地解释一下。

想象一下,你在一个与你目前居住的地方不同的地方找到了一份不错的工作,你想要搬迁,因为新公司要求这样做,并支付费用。你们有最现代的电视、冰箱、超柔软的真皮沙发、床等等。你雇了一家搬家公司,他们来了,把所有东西拆开,方便地打包。他们还确保尽可能多地打包在集装箱里,以便装满卡车,这样他们就可以在一次旅行中做到这一点。一旦他们到达目的地,他们就会打开行李,组装,并将所有东西恢复原样。

同样的道理也适用于数据。当我在Minio中存储一些数据时,我需要将其提供给另一个应用程序,比如说Spark,消费应用程序需要从Minio数据湖中拆分数据,打包并通过有线(或无线)传输,接收、解包和重新组装。

让我们使用更多的技术术语来描述这种拆卸和组装--数据的序列化和反序列化。不幸的是,这两个过程都很复杂和耗时。下面是一个简短的图表,说明当Apache Spark读取数据时,它会发生什么情况。

您以前可能没有注意到这个问题。假设Minio位于网络上的一台(或多台)计算机上。我们编写了一个Spark Map-Reduce应用程序。即使网络限制为100 GbE,我们获得的速度也几乎低于10 GbE。那么这个高速网络有什么用呢?不允许我们充分利用网络潜力(或至少70%-80%)的潜在问题是什么?

问题出在Spark检索数据的方式上。看看数据必须经过的层数。这对我们可以实现的吞吐量造成了限制。像Apache Cail这样的项目就是为解决这些问题而设计的。

如果我们想一想上面提到的搬迁例子,我们会看到物流公司永远不会接受沙发的原样,他们会为了便于运输而拆解它。请注意,这只是为了运输目的-如果目标不同,那么拆卸沙发可能不是正确的方法。鉴于数据湖的目标是分析,而不是交易需求,我们必须考虑到这一点。对于事务,我们通常使用Oracle或Postgres等OLTP系统-因为它们特别适合这项工作。快速回顾一下OLAP的分析需求可能是合乎情理的。

让我们从最著名的RDMBS表之一-Oracle表开始。顶部显示了数据如何作为";关系";或";元组";存储在RDBMS中。我们叫它桌子。我给你提供两个问题。

第一个是事务性查询。它必须扫描表格上的每一行,并找出员工的姓名,无论该工作是办事员。第二个是分析性查询--目标是一般结果,而不是原子结果。不幸的是,如果我们使用RDBMS表示数据的方式,第一个和第二个查询必须扫描所有行。如果数据大小为20 GB,将扫描所有大于或小于20 GB的数据。这是上图的上半部分。

让我们做一些修改--把我们所有的列都排成行。就像矩阵的转置一样-查看上图的底部,您的数据将是什么样子。在此转置之后,整个块仅表示一列。第二个分析查询需要扫描多少块?只有一个区块,大概有2 GB大小。

差别很大吗?柱状表示是ORC(优化行列)和拼图文件中使用的表示形式,其目标是加快分析速度。

列格式更易于阅读,然而,它们带来了另一个问题-它们通常以压缩格式存储。因此,消费应用程序需要在读取时将其解压缩,并在写入时将其压缩回去。

让我简要地解释一下软件系统中的读/写是如何发生的,以及硬件扮演什么角色。

微处理器通常使用两种方法连接外部设备:内存映射或端口映射I/O。

内存映射I/O映射到与程序内存和/或用户内存相同的地址空间,并以相同的方式进行访问。

端口映射I/O使用单独的专用地址空间,并通过一组专用微处理器指令进行访问。

在内存映射方法中,I/O设备与RAM和ROM一起映射到系统内存映射中。要访问硬件设备,只需使用正常的内存访问指令读取或写入这些特殊地址即可。这种方法的优点是,每条可以访问内存的指令都可以用来操作I/O设备。

通常,应用程序使用端口映射I/O。如果我们对特定格式使用内存映射I/O,则速度会更快,特别是对于分析需要。当与我们的列式数据格式结合使用时,它将变得更加有利。

Arrow使用内存映射I/O,并且在利用列数据格式在大多数格式之间进行转换时避免了序列化/反序列化开销。

多亏了Wes McKinney这一出色的创新,这样的想法来自他和他的团队并不令人惊讶,因为他作为Python中熊猫的创造者而广为人知。他称Arrow是数据传输的未来。

我们打算将数据存储在Arrow中,然后让使用它的应用程序读取它-从而显著提高速度。第一步让我们将数据以Arrow格式放入Minio。我一直在使用自己的方法,直到我看到Bryan Cutler提供了更好的实现,他的贡献还包括将Arrow格式集成到Spark中。

我们将从一个.csv文件开始,在本例中是从Movielens站点下载的电影评级。出于演示目的,我取了大约100K行。首先,让我们编写一个Spark程序来读取此CSV文件,并使用Arrow RDD将其写入Arrow格式。您可以从本文底部的链接中获得完整的代码。

ArrowRDD类有一个迭代器和RDD本身。要创建自定义RDD,本质上必须覆盖mapPartitions方法。您可以浏览代码以了解详细信息。

正如您从代码中看到的,Arrow格式文件是在数据目录中生成的。让我们将其复制到我们之前创建的Minio存储桶中(存储桶名称为arrowbucket)。

使用您最喜欢的Python编辑器,并编写一些代码。首先,让我们从Spark读取文件并将其转换为DataFrame开始,使用和不使用启用箭头的选项。

启动您的星火簇。使用所有设置完成代码,并检查我们是否成功创建了Spark上下文。要确保我们的应用程序(第8行命名为Minio-Arrow-Spark)已连接,只需检查Spark UI即可。您应该会看到类似这样的内容:

显示时间的输出显示了此方法的威力。性能提升是巨大的,几乎是50%。

回想一下,我们在前面创建了一个ArrowRDD,并使用它来写入Minio。让我们测试一下读取它时的内存消耗。我们将使用不同的方法。

我们正在读取不同的文件格式,并看到每种格式的内存消耗。很明显,基于Arrow格式的文件是零拷贝的,几乎不会消耗任何内存。

通过将Minio与Arrow格式相结合,您可以增强您的分析生态系统,并且几乎消除了与不同格式之间转换相关的摩擦。这主要是由于减少了序列化开销。

拉维尚卡尔·奈尔(Ravishankar Nair)是一位技术布道者、顾问和鼓舞人心的演说家。他是总部设在佛罗里达州的PassionBytes的首席技术官。凭借其在数据工程方面的丰富专业知识,Ravi提供机器学习、现代数据湖和分布式计算技术方面的咨询服务。你可以在这里参考他其他与Minio相关的文章: