架构师数据和文件格式指南

The Architect’s Guide to Data and File Formats

这篇文章最初发表在 The New Stack 上。

数据世界正在发生一场革命。在技术进步的推动下,当前的开源数据格式浪潮正在改变整个生态系统——从供应商到企业。

数据需要组织和分析才能产生及时见解,帮助公司做出更明智的决策并改进运营。处理非结构化数据(如图像、.pdf、音频、视频等)会带来不同的挑战。结构化和半结构化数据(如 CSV、XML、JSON 等)难以压缩、优化和长期存储。
从这些数据集生成见解的能力取决于数据的组织方式。由于企业记录的业务数据日益复杂,他们可能会发现,他们不再为每个数据事件收集 20 个字段,而是收集数百个字段。虽然这些数据很容易存储在数据湖中,但如果以基于行的格式存储,查询它将需要扫描大量数据。

使用数据处理、人工智能/机器学习 (AI/ML)、商业智能和报告工作负载查询和操作数据的能力变得至关重要。这些工作负载必然需要从庞大的数据集中获取较小的数据范围。因此,需要数据格式。

如果你与任何数据工程师互动过,你一定听说过不同的数据格式,如 Parquet、ORC、Avro、Arrow、Protobuf、Thrift、MessagePack 等。这些文件和数据格式是什么?它们适用于什么场景?如何为正确的工作选择正确的设计?这些是我们将在本文中解释的一些问题。

微服务的数据格式

在深入研究数据湖中高效存储和检索的数据格式之前,让我们看一下与数据湖不太相关的布局。Protobuf、Thrift 和 MessagePack 等数据格式更适用于微服务之间的互通。

Protocol Buffers

Google 的 Protocol Buffers(也称为 Protobuf)是一种与语言和平台无关的数据序列化格式,用于有效地编码结构化数据,以便通过网络传输或存储在文件中。它被设计为可扩展的,允许用户定义自己的数据类型和结构,并提供一种紧凑的二进制表示形式,可以有效地传输和存储。Protocol Buffers 用于各种 Google API,并且在许多编程语言中得到支持,包括 C++、Python 和 Java。Protocol Buffers 通常用于需要通过网络传输或以紧凑格式存储数据的场景,例如在网络通信协议、数据存储和数据集成中。

实施概述

gRPC 是一种现代的开源高性能 RPC(远程过程调用)框架,可以在任何环境中运行。它使客户端和服务器应用程序方法之间的交互直接进行,类似于面向对象编程中的方法调用。gRPC 建立在 HTTP/2 之上,作为传输协议和 ProtoBuf 框架,用于对请求和响应消息进行编码。gRPC 被设计为高效的,支持双向流和低延迟通信,并且可以在任何环境中运行。它用于各种 Google API,并且在许多编程语言中得到支持,包括 C++、Python 和 Java。

gRPC 用于各种需要低延迟、高性能和高效通信的上下文和应用程序。它通常用于连接多语言系统,例如连接移动设备、Web 客户端和后端服务。它还用于微服务架构来连接服务以及构建可扩展的分布式系统。此外,gRPC 用于物联网 (IoT) 应用程序、实时消息传递和流式传输以及连接云服务。

来源:https://developers.google.com/protocol-buffers

Thrift

另一种类似于 Protobuf 的数据格式是 Thrift。Thrift 是一种接口定义语言 (IDL) 和通信协议,允许开发可跨多种编程语言使用的可扩展服务。它类似于其他 IDL,例如 CORBA 和 Google 的 Protocol Buffers,但被设计为轻量级且易于使用。Thrift 使用代码生成方法,根据 IDL 定义为所需的编程语言生成代码,从而使开发人员能够轻松构建可以使用 Thrift 的二进制通信协议相互通信的客户端和服务器应用程序。Thrift 用于各种应用程序,包括分布式系统、微服务和面向消息的中间件。

支持的语言

Apache Thrift 支持多种编程语言,包括 Erlang 和 Haskell 等函数式语言。这使你能够用一种语言定义服务,然后使用 Thrift 为其他语言生成必要的代码以实现该服务,以及可用于从另一种语言调用该服务的客户端库。这使得构建使用多种编程语言的互连系统成为可能。

实施**概述**

IDL 编译器生成的代码创建客户端和服务器存根,这些存根在幕后通过本机协议和传输层进行交互,从而实现进程之间的 RPC。

来源:https://thrift.apache.org/

MessagePack

MessagePack 是一种数据序列化格式,提供结构化数据的紧凑二进制表示形式。与 JSON 等其他序列化格式相比,它被设计为更高效、更快速,因为它使用数据的二进制表示形式而不是基于文本的表示形式。MessagePack 可用于各种应用程序,包括分布式系统、微服务和数据存储。它在许多编程语言中得到支持,包括 C++、Python 和 Java,并且通常用于需要通过网络传输或以紧凑格式存储数据的场景。除了效率和速度之外,MessagePack 还被设计为可扩展的,允许用户定义自己的数据类型和结构。

支持的语言

MessagePack 支持多种编程语言,这主要归功于它的简单性。请参阅其门户网站上的实施列表

实施概述

我们在MinIO 选择了MessagePack 作为我们的序列化格式。它通过允许添加/删除键来保持与 JSON 的扩展性。初始实现是一个标题,后跟一个具有以下结构的 MessagePack 对象

{
  "Versions": [
    {
      "Type": 0, // Type of version, object with data or delete marker.
      "V1Obj": { /* object data converted from previous versions */ },
      "V2Obj": {
          "VersionID": "",  // Version ID for delete marker
          "ModTime": "",    // Object delete marker modified time
          "PartNumbers": 0, // Part Numbers
          "PartETags": [],  // Part ETags
          "MetaSys": {}     // Custom metadata fields.
          // More metadata
      },
      "DelObj": {
          "VersionID": "", // Version ID for delete marker
          "ModTime": "",   // Object delete marker modified time
          "MetaSys": {}    // Delete marker metadata
      }
    }
  ]
}

元数据转换来自以前版本,新版本包括一个“V2Obj”或“DelObj”,具体取决于收到更新请求时的活动操作。本质上,在仅需要读取元数据的情况下,我们可以仅仅在到达元数据末尾时停止读取文件。我们可以通过最多两次连续读取来实现更新。

磁盘上的表示形式也发生了改变以适应这一点。以前,所有元数据都以一个包含所有版本的大型对象的形式存储。现在,我们将其写入如下

  • 带有版本的签名
  • 标题数据的版本(整数)
  • 元数据的版本(整数)
  • 版本计数(整数)
xl.meta 的总体结构

如果你想更好地了解 MessagePack 在 MinIO 中的版本控制是如何工作的,请阅读这篇优秀的博文

流式传输的数据格式

Avro

Apache Avro 是一种数据序列化系统,提供数据结构的紧凑、快速的二进制表示形式。它被设计为可扩展的,允许用户定义自己的数据类型和结构,并提供对动态类型和静态类型语言的支持。Avro 通常用于 Hadoop 生态系统,并用于各种应用程序,包括数据存储、数据交换和数据集成。Avro 使用基于模式的方法,其中为数据定义一个模式,并用于序列化和反序列化数据。这使得 Avro 可以支持丰富的数据结构和随着时间的推移对数据进行演变。Avro 还支持容器文件以存储持久数据、RPC 以及与多种语言的集成。

Avro 依赖于模式。在写入 Avro 数据时,Avro 模式会保存在文件中。Avro 模式是一个 JSON 文档,它定义了存储在 Avro 文件中或在 Avro 消息中传输的数据的结构。它定义了数据中字段的数据类型,以及这些字段的名称和顺序。它还可以包括有关数据的编码和压缩的信息,以及与数据关联的任何元数据。

Avro 模式用于确保数据以一致且可预测的格式存储和传输。当数据写入 Avro 文件或在 Avro 消息中传输时,模式会与数据一起包含在内,以便数据接收者知道如何解释它。

模式方法允许序列化既小又快,同时支持动态脚本语言。

文件以后可以由任何程序处理。如果读取数据的程序期望不同的模式,则可以快速解决,因为两种模式都存在。

支持的语言

Apache Avro 是记录数据的领先序列化格式,也是流式数据管道的首选。它提供出色的模式演变,并具有针对 JVM(Java、Kotlin、Scala、…)、Python、C/C++/C#、PHP、Ruby、Rust、JavaScript 甚至 Perl 的实现。

要详细了解 Avro 与其他系统的比较情况,请查看文档中的有用比较

Apache Kafka 和Confluent Platform 为 Avro 建立了一些特殊的连接,但它可以与任何数据格式一起使用。

数据湖的大数据文件格式

Parquet

Apache Parquet 是一种用于大数据处理的列式存储格式。它旨在高效地进行存储和处理,并且在 Hadoop 生态系统中被广泛用作数据存储和交换格式。尽管 Hadoop 的使用率有所下降,但由于 Apache Spark、Apache Flink 和 Apache Drill 等关键数据处理系统对该格式的持续支持,该格式仍然具有相关性并被广泛使用。

Parquet 以列式格式存储数据,以优化列操作(如过滤和聚合)的方式组织数据。Parquet 使用压缩和编码技术的组合以高度高效的方式存储数据,并允许创建可用于实施数据类型约束和实现快速数据处理的数据模式。Parquet 是用于存储和查询大型数据集的流行选择,因为它可以实现快速查询和高效的数据存储和处理。

实施概述

这种模式允许高效地捕获元数据,能够使文件格式演进并简化数据存储。Parquet 压缩算法减少了存储需求,能够实现更快的检索,并且受到许多框架的支持。元数据有三种类型:文件元数据、列(块)元数据和页面头元数据。Parquet 利用 Thrift 的 TCompactProtocol 来对元数据结构进行序列化和反序列化。

来源:https://parquet.apache.org

ORC

ORC(或优化行列)是一种数据存储格式,旨在通过优化大量数据的存储和检索来提高数据处理系统(如 Apache Hive 和 Apache Pig)的性能。ORC 以列式格式存储数据,这意味着数据按列而不是按行组织和存储。这允许更快地查询和分析数据,因为只需要访问相关的列,而不是读取整个数据行。ORC 还包括压缩、谓词下推、使用单独的 RecordReaders 对相同文件进行并发处理以及类型推断等功能,以进一步提高性能。

与 RCFile 格式相比,ORC 在 Hadoop 部署中的另一个改进领域是它显着减少了对 NameNodes 的负载。

需要注意的是,ORC 是针对 Hadoop 架构和工作负载进行优化的。鉴于大多数现代数据堆栈正在远离 Hadoop,ORC 在云原生环境中用途有限。

实施概述

ORC 文件中的行数据被组织成称为条带的组,这些组包含一系列行和有关这些行的元数据。每个条带包含一系列行,每个行被分成一系列列。每列的数据分别存储,这允许 ORC 有效地仅读取特定查询所需的列,而不是读取整个行。ORC 还包括有关数据的元数据,例如数据类型和压缩信息,这有助于提高读取性能。ORC 支持各种压缩格式,包括 zlib、LZO 和 Snappy,这有助于减少磁盘上的数据大小并提高读写性能。它还支持各种索引类型,例如行索引和布隆过滤器,这些索引可用于进一步提高读取性能。

来源:https://orc.apache.org

orcfiledump 的输出包括文件元数据的摘要,例如文件中的行数、列数和条带数。它还包括列的数据类型以及存储在文件中的任何索引信息。

除了元数据之外,orcfiledump 还打印 ORC 文件中的实际数据。这包括每行中每列的值,以及存在的任何 NULL 值。每行代表一条记录,该记录由定义为字段的每列组成。这些行组合起来代表一个表格结构。

Arrow

Apache Arrow 相对较新,它是一种开源的内存列式数据格式,旨在加速分析和数据处理任务。它是一种标准化格式,用于在各种系统(包括数据存储系统、数据处理框架和机器学习库)中表示和操作数据。

Apache Arrow 的主要优势之一是它能够在不同的系统和进程之间高效地传输数据。它允许在系统之间共享和交换数据,而无需进行序列化或反序列化,这可能非常耗时且资源密集型。这使得它非常适合用于分布式和并行计算环境,在这些环境中需要快速处理和分析大量数据。

除了性能优势之外,Apache Arrow 还具有丰富的功能集,包括对各种数据类型的支持、对嵌套和层次数据结构的支持,以及与各种编程语言的集成。Arrow 格式通常用于大数据和分析应用程序,在这些应用程序中,它可以用于在系统之间高效地传输数据并在内存中处理数据。它还用于开发分布式系统和实时分析管道。

实施概述

大多数数据团队面临的挑战是,在没有标准列式数据格式的情况下,通过序列化/反序列化来处理每个数据库和语言的内部数据格式会导致效率低下和性能问题。另一个主要挑战是为每种数据格式编写标准算法,这会导致重复和膨胀。

来源:https://arrow.apache.ac.cn/

Arrow 的内存列式数据格式通过减少对磁盘的 IO 读取和写入以及启用查询加速来显着增强数据分析。它使用数据的固定宽度二进制表示,这允许在内存中快速读取和写入数据。除了由于其内存计算而提高操作效率外,该框架还允许添加自定义连接器,以简化跨各种格式的数据聚合。

来源:https://arrow.apache.ac.cn/overview/

Dremio 是基于 Arrow 框架构建的查询加速工具。Dremio 旨在为各种数据源(例如数据库、数据湖和云存储系统)提供快速的查询性能。它使用 Arrow 内存列式数据格式来存储和处理数据,这使其能够利用 Arrow 的性能和效率优势。有关更多信息,请参阅“Apache Arrow 及其在当今数据环境中的地位”。

因地制宜 — 选择最佳格式

组织已投资于微服务架构以更好地进行软件管理,摒弃了单体方法。采用容器化并在大型 Kubernetes 集群上进行部署是一个自然演变。这些微服务是用异构语言构建的,并且采用了多种语言和多种范式。

在这些情况下,采用 Protocol Buffers、Thrift 或 MessagePack 非常有用。它们简化了微服务之间的互通信,并能够实现更快的事件处理。有助于提高应用程序可支持性的其他好处包括能够独立部署和频繁部署。

随着流式和消息驱动架构的出现,对数据格式和压缩的广泛需求使 Avro 成为首选。如上所述,Arvo 非常灵活,可以在微服务、流式应用程序和消息驱动架构中采用,并且在数据湖和湖仓架构中得到了广泛的应用。像 Iceberg 和 Hudi 这样的开放式表格格式利用 Avro 与模式一起使用,从而能够实现快照隔离。

Parquet 在数据湖和湖仓架构中具有领先的采用率,并且仍然是该领域的一种标准。ORC 支持大型条带的能力使其在 Hadoop 分布式文件系统 (HDFS) 世界中得到了广泛的应用,但众所周知,该世界正在缩小。尽管如此,它仍然对备份用例有用。Arrow 是这个领域的新成员,非常适合内存中使用,并且可以用于对象存储用例,其中对象持久性需求较短。

结论

现代数据堆栈是一种选择,在数据和文件格式方面,越来越多的选择。MinIO 支持所有这些格式,将选择权留给您和您的云架构师决定使用哪种格式。随时可以通过下载 这里 或查看我们的 Slack 频道 或通过 商业许可 直接与工程团队互动来试用它们。