作为我们在我们的平台上创建更好用户体验的努力的一部分,我们的地图数据收集团队的成员使用专用的移动应用程序来收集图像及其相关的元数据增强我们的地图。例如,我们的团队将街道标志的图像捕获到提高地图数据的效率和质量为了促进骑手和司机合作伙伴更无缝的旅行体验,以及拍摄在优步展示应用程序的食物的照片。然后,我们通过一系列Apache Spark作业处理图像和元数据,并将结果存储在我们的Hadoop分布式文件系统(HDFS)实例中。
其中一个地图数据采集团队最大的项目需要摄取和处理超过80亿图像。下游Apache Spark作业所需的图像以及负责地图编辑的运算符随机访问。虽然我们为我们的Apache Spark架构做出的设计选择需要我们处理数十亿图像,但结果模式也已应用于具有数千个图像规模明显较小的项目的项目。
Apache Spark支持许多文件格式,允许将多个记录存储在单个文件中。每个文件格式都有自己的优点和缺点。
在本文中,我们概述了文件格式的地图数据收集团队用于处理大量的图像和元数据,以优化下游消费者的体验。我们希望您发现这些外卖器可用于您自己的数据分析需求。
使用HDFS的Apache Spark
图像和元数据通过优步开发的专用移动应用程序收集。一旦数据被捕获,移动应用程序将数据上传到云存储。图像和元数据从云存储输入到优步数据中心,然后进行处理:
- 摄取:将原始数据级分为数据中心,以便并行处理。
- 过程/变换:一系列步骤,解除原始数据并执行处理以将数据完善为下游消费者的有用信息。
以下文件格式用于每个输出类型。
- 摄入数据:sequencefiles.为BLOB数据提供有效的写入。
- 中间数据:Avro提供丰富的架构支持和比镶木地板更高效的写字,特别是对于BLOB数据。
- 最终产出: 的组合par那Avro, 和杰森文件
每种文件格式都有优缺点,每种输出类型都需要支持一组独特的用例。对于每种输出类型,我们选择了优点最大化、缺点最小化的文件格式。在下面的部分中,我们将解释为每种格式类型选择每种文件格式的原因。
摄入数据
“摄取”(Ingestion)是优步开发的手机应用程序记录的数据有效地移动到数据中心进行进一步处理的过程。Ingest从外部读取数据并写入HDFS,这样可以通过Spark job高效处理文件。在HDFS中,存储少量的大文件比存储大量的小文件更能节省namenode上的内存资源,提高Spark作业处理这些文件的效率。(如果你有兴趣了解更多这个话题,我们建议你去看看本文在Cloudera工程博客上)。雷竞技到底好不好用
在摄入期间,未更改文件的内容,以便通过火花并行地进行额外的处理和变换。对处理和转换负责的火花作业,整体读取数据,并且对无滤波很少。因此,使用简单的文件格式,提供最佳写性能,并且没有架构的文件格式的开销,例如Apache Avro和Apache Parquet。
摄取的每个实例都将文件写入单个HDFS lequenceFile,从而导致一些大文件是最佳的HDFS。SequenceFile是一个包含二进制键/值对组成的平面文件。API允许将数据存储在二进制键或其他数据类型中,例如字符串或整数。在Invest的情况下,密钥是包含文件的完整路径的字符串,该值是包含摄入文件内容的二进制数组。
中间数据
一旦数据被摄取,一系列Spark作业将对数据进行转换并执行其他处理。本系列中的每个Spark作业都写中间数据。由前一个任务生成的中间数据通常全部读取,既不按列也不按行过滤。对于这种类型的中间数据,我们通常使用Avro文件格式。
Spark默认的文件格式为“Parquet”。Parquet有许多优点,可以提高查询和过滤数据的性能。然而,这些优点伴随着编写文件时的前期成本。Parquet是一种基于列的文件格式,这意味着存储在物理文件(也称为块)中的所有行中给定列的所有值在写到磁盘之前被组合在一起。因此,在写入区块之前,所有的记录都需要存储并重新安排在内存中。相反,Avro文件是一种基于记录的文件格式,可以更有效地将记录流到磁盘。(拼花的网站上有一系列录制的演示文稿这提供了更全面的基于柱状文件格式的解释。)
由于较低的写开销,Avro文件通常用于存储中间数据:
- 数据写一次并阅读一次。
- 读取器不会过滤数据。
- 读者顺序读取数据。
最终产出
最终输出是已处理的数据,并由多个下游消费者访问。每个消费者都有独特的要求,通常通过查询和过滤数据来解决。例如,消费者可以对特定类型的图像执行地理空间查询。最终产出分为三类:
- 图像元数据:关于图像的数据,如从中拍摄的位置。
- 意像:传感器捕获的图像。
- 汇总数据:关于一组图像的高级数据,例如用于处理Imagery或总比数的软件版本的版本。
图像元数据
与中间数据不同,图像元数据是多次读取并经常过滤和查询。数据存储在木质中,因为优势显着超过了额外的写开销。以下是两项性能测试的结果,可查询大约500万条记录。
第一个查询是一个简单的边界框查询图像的纬度和经度返回大约250,000条记录。从下游消费者的角度来看,镶木地板查询速度速度速度速度速度更快。然而,真正的影响是基础设施。
最大的改进是查询所需的I / O所需的I / O,其中镶木地板消耗AVRO查询所需的I / O.7.5%。Parquet文件格式存储显着减少读取的数据量的统计信息。
| 资源 | Avro | par | 改进 |
| 墙时间(秒) | 20.76 | 7.17 | 290% |
| 核心时间(分钟) | 24.80. | 1.28 | 1,938% |
| 读(MB) | 24678年。4 | 1,848.5. | 1,335% |
第二个查询是返回大约40,000条记录的字符串比较。同样,对Parquet文件的查询性能明显优于对Avro文件的查询。与等效的Avro查询相比,Parquet查询的执行速度快了两倍,需要1.5%的I/O。
| 资源 | Avro | par | 改进 |
| 墙时间(秒) | 18.48 | 6.0 | 308% |
| 核心时间(分钟) | 1670.00 | 50.76 | 3,289% |
| 读(MB) | 24678年。4 | 376.6 | 6,552% |
意像
Parquet不适用于存储大型二进制数据,例如图像,因为它是一种以柱状格式排列大型二进制数据的资源密集型进程。另一方面,Avro适用于存储图像。但是,如前一节中所讨论的,Avro对查询不是最佳的。
为了支持查询,将两列添加到Imagery Metadata Parquet文件中,以用作图像的外键。这允许客户端通过元数据查询图像。
需要涵盖两个关键的细节,以了解如何实现交叉引用:
- 部分文件:Spark Subdivides数据分为分区,并且当数据写入Avro时,每个分区都被写入单独的零件文件。
- 记录偏移:Avro API支持将偏移量获取到存储特定记录的文件中。给定偏移量,Avro API可以有效地寻求文件位置并读取记录。此功能可通过本机Avro API获得,而不是通过Spark Wrapper API进行Avro。
附加在图像元数据后面作为对图像的交叉引用的两列是存储图像记录的部件文件的名称和部件文件中记录的文件偏移量。由于用于Avro文件的Spark包装器API并不公开记录偏移量,因此必须使用本机Avro API来编写图像。
图像写在火花地图中。实现将根据火花的版本而异,以及是否使用DataFrame或弹性分布式数据集API,但概念是相同的。通过Spark Map调用在每个分区上执行方法,并使用本机Avro API编写一个包含分区中包含的所有图像的单个部分文件。一般步骤如下:
- 读取摄取序列文件。
- 映射摄取序列文件的每个分区并将分区ID传递到地图函数。对于RDD,调用RDD.MAPPARTINGSWITHINDEX()。对于DataFrame,您可以通过Spark_Partition_ID(),通过df.growbykey()通过分区ID获取分区ID,然后调用df.flatmapgroups()。
- 在地图中函数执行以下操作:
- 创建一个标准的Avro Writer(不是Spark),并在文件名中包含分区id。
- 遍历摄取SequenceFile的每条记录,并将记录写入Avro文件。
- 呼叫DataFileWriter.sync()在Avro API中。这将刷新记录到磁盘并返回记录的偏移量。
- 通过映射函数的返回值以及您想要从图像中提取的任何其他元数据一起传递文件名和记录偏移量。
- 将生成的DataFrame或RDD保存到Parquet格式。
结果是Avro和伴侣镶木地板文件。Avro文件包含Imagery,Companion Parquet文件包含Avro文件路径和记录偏移集,以便在给定图像记录上有效地执行Avro文件上的搜索。查询的常规模式然后读取图像记录是:
- 查询拼影文件。
- 在结果中包含文件路径和偏移量。
- 可以选择重新分区结果,以优化读取图像记录时的并行度。
- 映射查询结果的每个分区。
- 在地图中函数执行以下操作:
- 为包含图像记录的Avro零件文件创建标准Avro Reader。
- 调用datafileader.seek(long)以读取指定偏移量的图像记录。
汇总数据
除了给定图像的元数据之外,我们还非常适合将聚合元数据存储在给定的Avro和Parquet文件对中存储的整个图像集。对于Uber的用例,聚合元数据的示例包括:
- 用于处理给定的图像集的管道版本。如果在管道中找到错误,则使用该数据用于有效地识别需要重新处理的图像。
- 收集图像的地理区域。这允许客户端识别要在地理空间搜索中包含哪些Avro和镶木地区文件对。
汇总数据以下列原因存储在JSON文件中:
- 调试:由于JSON文件是格式化的文本,并且通常包含少量记录,因此可以轻松显示它们而无需代码或特殊工具。
- “高效”读:在许多情况下,JSON文件将包含给定的Avro和Parquet对的单个记录。Parquet和Avro都有开销,因为两种文件格式都包含头信息。JSON没有这种开销,因为格式缺少头信息。
- 参照完整性:替代方案是将汇总记录存储在数据库中。但是,如果给定的一组图像集的JSON,AVRO和PALQUET文件存储在单个父目录中,则可以通过使用单个原子HDFS操作移动父目录来归档图像,图像元数据和聚合元数据。
关键的外卖
Spark的默认文件格式是Parquet,但正如我们上面讨论的,也有其他格式更适合的用例,包括:
- sequencefiles.:当不需要富模式支持的开销时,二进制键/值对是blob存储的好选择
- par:支持高效查询、强类型模式,并具有其他好处的数量本文未涵盖
- Avro:适用于大型二进制数据或下游消费者全部读取记录并支持随机寻求访问记录。提供定义强类型模式的能力。
- 杰森:当记录存储在多个小文件中时
通过为Spark作业选择最优的HDFS文件格式,可以确保作业有效利用数据中心资源,最好地满足下游用户的需求。
如果你对大规模数据处理挑战或计算机视觉技术感兴趣,可以考虑申请一个职位我们的博尔德,基于CO-CONEXING COUND!!






