使用Apache Arrow对MinIO 数据湖进行涡轮增压

使用Apache Arrow对MinIO 数据湖进行涡轮增压


基于我们几年前所做的一些工作,越来越多的企业已经开始或已经实施了数据湖战略。如果您想花点时间查看-您可以在此处此处找到这些帖子

目的

在本文中,我将解释一种加速MinIO使用的机制。就MinIO而言,什么都没有改变,优化将在数据的基础存储上进行。我们将选择一种最新的格式来全面提高敏捷性。我们将展示您的数据湖数据如何在不经历任何“转换”时间的情况下跨系统传输的方式。

阿帕奇箭

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

想象一下,由于新公司的要求并为此付出了代价,您在与当前居住地不同的位置获得了一份不错的工作,并且想搬迁。您拥有最现代的电视,冰箱,超软皮沙发,床等。您需要与一家搬家公司联系,该公司会来,将所有东西拆解,然后方便地打包。他们还确保将尽可能多的容器包装在装满卡车的卡车中,这样他们就可以一次完成运输。一旦到达目的地,他们便会拆封,组装和恢复一切。

数据同样如此。当我将某些数据存储在MinIO中时,我需要将其提供给另一个应用程序(例如Spark),使用中的应用程序需要从MinIO数据湖中分解数据,打包并通过有线(或无线)传输,接收,打开包装并重新组装。

让我们使用更多的技术术语来进行反汇编和组装-数据的序列化和反序列化。不幸的是,这两个过程都是复杂且耗时的。这是一个简短的图,说明了Apache Spark读取数据时发生的情况


Screen-Shot-2020-09-06-at-1.54.30-AM.png
实验装置。礼貌:Spark Summit


您可能以前没有注意到此问题。假设MinIO在网络上的计算机上。我们编写一个Spark Map-Reduce应用程序。尽管网络限制为100 GbE,但我们获得的速度几乎不到10 GbE。那么,这个高速网络有什么用?潜在的问题是什么使得我们无法充分利用网络的全部潜力,或者至少发挥其70-80%的潜力?


Screen-Shot-2020-09-06-at-2.00.34-AM.png
其他层,缓冲区和序列化器


问题在于Spark检索数据的方式。查看数据必须通过的层数。这限制了我们可以达到的吞吐量。诸如Apache Crail之类的项目旨在解决这些问题。

优化:列式数据格式

如果我们考虑上面提到的搬迁示例,我们会发现物流公司将永远不会拿走沙发,他们会分解沙发以便于运输。请注意,这仅是出于运输目的-如果该目的不同,则拆卸沙发可能不是正确的方法。

鉴于数据湖的目标是分析-而不是交易需求,我们必须考虑这一点。对于事务,我们经常使用OLTP系统,例如Oracle或PostGres-鉴于它们特别适合该工作。快速浏览OLAP的分析要求可能是有必要的。


Picture1.png
引入列格式


让我们从最著名的RDMBS表之一开始-Oracle的“ emp”表。顶部显示了如何将数据作为“关系”或“元组”存储在RDBMS中。我们称它为桌子。我为您提供两个查询

  1. 从emp中选择ename,其中job ='CLERK'

  2. 从emp中选择总和(sal)

第一个是事务查询。它必须扫描表上的每一行,并找出在哪里工作的职员的姓名。第二个是分析查询-目标不是一般结果,而是一般结果。不幸的是,如果我们使用RDBMS表示数据的方式,则第一个和第二个查询必须扫描所有行。如果数据大小为20 GB,则将扫描所有20 GB或多或少的数据。这是上图的顶部。

让我们进行一些更改-将所有列都放入行中。就像矩阵的转置一样,请参见上图的底部,您的数据将如何显示。进行此转置后,整个块仅代表一列。第二个分析查询需要扫描多少个块?仅有一个块,可能约为2 GB。

区别显着吗?柱状表示法是ORC(优化行柱状)和Parquet文件中使用的格式,目的是使分析更快。

列格式更易于阅读,但是,它们又带来了另一个问题-它们通常以压缩格式存储。结果,使用中的应用程序将需要在读取时将其解压缩,并在写入时将其压缩回去。

请注意这一点,因为稍后我们将重新讨论这一点。

读/写数据的科学

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

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

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

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

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

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

欢迎使用Apache Arrow

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

谢韦斯·麦金尼Wes McKinney)的这项杰出创新,他和团队提出了这样的想法并不奇怪,因为他是Python中Pandas的创造者。他称Arrow为数据传输的未来。

以箭头格式将数据存储在MinIO中

这就是我们要使MinIO变得更加强大的方式。

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

我们将从一个.csv文件开始,在这种情况下,是从movielens网站下载的电影分级出于说明目的,我花了大约10万行。首先,让我们编写一个Spark程序来读取此CSV文件,并使用Arrow RDD将其写入Arrow格式。您可以从本文底部的链接中获取完整的代码。

步骤1:build.sbt,请注意箭头依赖项


Screen-Shot-2020-09-06-at-4.38.17-AM.png
参见第18和19行,我们将Spark与Arrow相关的依赖项


我们将使用Spark 3.0和Apache Arrow 0.17.1

ArrowRDD类具有迭代器和RDD本身。要创建自定义RDD,本质上您必须重写mapPartitions方法。您可以浏览代码以获取详细信息。

接下来,启动MinIO并创建一个名为“ arrowbucket”的存储桶。


Screen-Shot-2020-09-06-at-4.26.37-AM.png
在MinIO中创建一个名为“ arrowbucket”的存储桶


让我们使用ArrowRDD在本地创建一个ArrowFile。这是代码:


Screen-Shot-2020-09-06-at-4.48.45-AM.png
用ArrowRDD编写Arrow文件


第22至34行是主要部分。编译并执行代码:


Screen-Shot-2020-09-06-at-4.50.42-AM.png
执行代码


从代码中可以看到,箭头格式文件是在数据目录中生成的。让我们将其复制到我们先前创建的MinIO存储桶中(存储桶名称为arrowbucket)


Screen-Shot-2020-09-06-at-5.04.23-AM.png
将我们生成的箭头文件复制到MinIO存储桶


让我们现在玩得开心。

使用您喜欢的Python编辑器,并编写一些代码。首先,让我们从Spark读取文件并将其转换为数据框开始,无论是否启用了Arrow选项。


Screen-Shot-2020-09-06-at-11.52.01-AM.png
初始化Spark上下文和Minio的连接参数


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


Screen-Shot-2020-09-06-at-11.46.01-AM.png
Spark UI(默认localhost:8080)显示我们的应用已连接


立即运行以下代码:


Screen-Shot-2020-09-06-at-11.09.47-PM.png
从MinIO读取箭头格式为“未启用”(顶部)和“已启用”(底部)的箭头


显示时间的输出显示了此方法的强大功能。性能大幅提升,几乎达到50%。

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


Screen-Shot-2020-09-06-at-11.17.22-PM.png
查看结果-箭头为零复制内存格式


我们正在读取不同的文件格式,并看到每种文件的内存消耗。显而易见,基于Arrow格式的文件为零拷贝-几乎完全没有消耗的内存。

通过将MinIO与Arrow Format结合使用,您可以增强分析生态系统并从根本上消除与在不同格式之间进行转换相关的摩擦。这主要是由于减少了串行化开销。



上一篇 下一篇