验证 HDFS 和 Cloud Storage 之间的数据传输

在不同的存储系统(如多个 Apache Hadoop 分布式文件系统 (HDFS) 集群)之间或在 HDFS 和 Cloud Storage 之间复制或移动数据时,建议执行某种类型的验证以确保数据完整性。为了确保数据在传输过程中未发生变化,此验证必不可少。

虽然各种机制已经确保传输中的点到点数据完整性(例如与 Cloud Storage 进行通信时使用的传输层安全协议 (TLS)),但显式的端到端数据完整性验证可以为典型传输机制未能检测到的情况增强保护。这有助于您检测由网络链接噪音、服务器计算机和路径上路由器中存在的内存错误或是软件 bug(例如客户使用的库中的软件 bug)引起的潜在数据损坏。

对于 Cloud Storage,此验证使用 gsutil cprsync 等命令在客户端自动进行。这些命令计算本地文件校验和,然后根据每个操作结束时由 Cloud Storage 计算的校验和进行验证。如果校验和不匹配,gsutil 会删除无效副本并输出一则警告消息。这种不匹配的情况很少发生,如果确实出现了这种问题,您可以重试操作。

现在,您可以使用另一种方法在 Apache Hadoop 中针对兼容 Hadoop 的不同文件系统(如 HDFS 和 Cloud Storage)自动执行端到端的客户端验证。本文介绍了如何使用这项新功能高效准确地比较文件校验和。

HDFS 如何处理文件校验和

HDFS 使用 CRC32C,后者是一种基于 Castagnoli 多项式的 32 位循环冗余校验码 (CRC),用于在不同环境中保持数据完整性:

  • 对于静态数据,Hadoop DataNodes 不断根据存储的 CRC 来验证数据,以检测和修复位衰减
  • 对于传输中的数据,DataNode 发送已知的 CRC 以及相应的批量数据,HDFS 客户端库协同计算每个 CRC 分块,并与 DataNode 接收的 CRC 进行比较。
  • 为便于 HDFS 进行管理,块级校验和用于对 DataNode 上的各个块文件进行低级手动完整性检查
  • 对于任意应用层用例,FileSystem 接口定义了 getFileChecksum,HDFS 实现使用其存储的细粒度 CRC 来定义文件级校验和。

对于大多数日常使用而言,CRC 在应用层中是透明使用的。唯一使用的 CRC 是 CRC32C 分块,它们已经预先计算并与块数据一起存储在元数据文件中。分块的大小由 dfs.bytes-per-checksum 定义,默认值为 512 字节。

Hadoop 默认文件校验和类型的缺点

默认情况下,在使用 Hadoop 时,所有面向 API 的校验和均采用 CRC32C 分块级联的 MD5 形式,要么在块级通过低级 DataTransferProtocol 实现,要么在文件级通过高级 FileSystem 接口实现。文件级校验和定义为所有块级校验和级联的 MD5 码,其中每个块级校验和是 CRC 分块级联的 MD5 码,因此被称为 MD5MD5CRC32FileChecksum。这实际上是一种按需的三层 Merkle 树

文件级校验和的这种定义对 HDFS 的实现及其具体数据布局很敏感,即分块大小(默认为 512 字节)和块大小(默认为 128 MB)。因此,这种默认文件校验和不适用于以下任何情况:

  • HDFS 中相同文件(但每个文件的块大小配置不同)的两个不同副本。
  • 配置了不同块或分块大小的两个不同 HDFS 实例。
  • HDFS 和非 HDFS 兼容 Hadoop 文件系统 (HCFS) 的组合,例如 Cloud Storage。

下图演示了采用不同文件系统配置的同一文件如何生成不同的校验和:

校验和不匹配

您可以使用 Hadoop fs -checksum 命令显示 HDFS 中的文件的默认校验和:

hadoop fs -checksum hdfs:///user/bob/data.bin

在块大小为 64 MB (dfs.block.size=67108864) 的 HDFS 集群中,该命令会显示如下结果:

hdfs:///user/bob/data.bin   MD5-of-131072MD5-of-512CRC32C   000002000000000000020000e9378baa1b8599e50cca212ccec2f8b7

对于块大小为 128 MB (dfs.block.size=134217728) 的另一个集群中的同一文件,您会看到不同的输出:

hdfs:///user/bob/data.bin   MD5-of-0MD5-of-512CRC32C    000002000000000000000000d3a7bae0b5200ed6707803a3911959f9

在上述示例中,您可以看到,同一文件的两个校验和有所不同。

Hadoop 全新组合式 CRC 文件校验和的工作原理

Apache Hadoop 3.1.1 发布了一个新的校验和类型以解决上述缺点,该类型反映在 HDFS-13056 中。由 dfs.checksum.combine.mode=COMPOSITE_CRC 配置的新类型将全新的组合式块级 CRC 和组合式文件级 CRC 定义为针对已存储分块 CRC 的数学组合式 CRC。使用此类型替换使用组合式 CRC 的 MD5 码,可以计算代表整个块或文件的单个 CRC,且其独立于分块 CRC 的较低级粒度。

CRC 组合具有许多优势:效率高;允许生成的校验和与分块或块完全无关;允许对分带文件和复制文件、不同 HDFS 实例,以及 HDFS 和其他外部存储系统进行差异对比。(如需详细了解 CRC 算法,请点击 PDF 下载。)

下图展示了采用不同文件系统配置传输文件时,如何确保其校验和的一致性:

校验和匹配

此功能的侵害性极低:它可以兼容现有的块元数据,无需更改分块验证的正常路径。这也意味着,即使是之前已存在的大型 HDFS 部署也可以采用此功能回溯性地同步数据。如需了解详情,您可以下载完整的设计 PDF 文档

使用全新的组合式 CRC 校验和类型

如需在 Hadoop 中使用全新的组合式 CRC 校验和类型,请将 dfs.checksum.combine.mode 属性设置为 COMPOSITE_CRC(而不是默认值 MD5MD5CRC)。将文件从一个位置复制到另一个位置时,分块级校验和类型(即 dfs.checksum.type 属性默认为 CRC32C)也必须在这两个位置匹配。

您可以将 -Ddfs.checksum.combine.mode=COMPOSITE_CRC 参数传递给 Hadoop fs -checksum 命令,来显示 HDFS 中的文件的新校验和类型:

hadoop fs -Ddfs.checksum.combine.mode=COMPOSITE_CRC -checksum hdfs:///user/bob/data.bin

无论 HDFS 集群的块大小配置如何,您都会看到相同的输出,如下所示:

hdfs:///user/bob/data.bin   COMPOSITE-CRC32C    c517d290

对于 Cloud Storage,您还必须将 Cloud Storage 连接器属性 fs.gs.checksum.type 明确设置为 CRC32C。否则,此属性默认为 NONE,会默认停用文件校验和。Cloud Storage 连接器的这一默认行为是一种避免 distcp 问题的预防措施,如果校验和类型不匹配而非正常失败,则会引发异常。该命令如下所示:

hadoop fs -Ddfs.checksum.combine.mode=COMPOSITE_CRC -Dfs.gs.checksum.type=CRC32C -checksum gs://[BUCKET]/user/bob/data.bin

该命令与前一 HDFS 上的示例显示的输出相同:

gs://[BUCKET]/user/bob/data.bin COMPOSITE-CRC32C    c517d290

您可以看到,无论块大小如何,也不管是否在 HDFS 和 Cloud Storage 之间传输数据,前面的命令返回的组合式 CRC 校验和均匹配。通过使用组合式 CRC 校验和,您现在可以确保在所有类型的 Hadoop 集群配置之间传输文件时保持数据完整性。

如果您正在运行 distcp,如下例所示,验证将自动执行:

hadoop distcp -Ddfs.checksum.combine.mode=COMPOSITE_CRC -Dfs.gs.checksum.type=CRC32C hdfs:///user/bob/* gs://[BUCKET]/user/bob/

如果在复制期间,distcp 检测到源和目标之间的文件校验和不匹配,则操作将失败并返回一则警告。

访问此功能

Apache Hadoop 3.1.1 中提供了全新的组合式 CRC 校验和功能(请参阅版本说明),向版本 2.7、2.8 和 2.9 的移植正处于准备阶段。自 2018 年末以来,此功能已默认包含Cloud Dataproc 1.3 的次要版本中。