擦除编码 101

Erasure Coding 101

擦除编码是分布式存储系统中一种关键的数据保护方法。这篇博文解释了擦除编码如何满足企业对数据保护的要求,以及它在 MinIO 中是如何实现的。

数据保护和硬件故障

数据保护在任何企业环境中都至关重要,因为硬件故障,特别是驱动器故障,非常常见。

传统上,使用不同类型的 RAID 技术或镜像/复制来提供硬件容错。镜像和复制依赖于一个或多个完整的数据冗余副本——这是一种代价高昂的存储消耗方式。更复杂的技术,如 RAID5 和 RAID6,提供了相同的容错能力,同时降低了存储开销。RAID 对于单个节点上的数据保护是一个不错的解决方案,但由于需要耗时的重建操作才能将失效的驱动器恢复到联机状态,因此无法扩展。

许多分布式系统使用三路复制来进行数据保护,其中原始数据完整地写入到 3 个不同的驱动器,并且任何一个驱动器都能够修复或读取原始数据。复制不仅在存储利用率方面效率低下,而且在从故障中恢复时操作效率也低下。当一个驱动器发生故障时,系统会将自身置于性能降低的只读模式,同时它会将完整驱动器完全复制到新驱动器上以替换失效的驱动器。

擦除编码

擦除编码应用于分布式存储的数据保护,因为它具有弹性和高效性。它将数据文件拆分为数据和奇偶校验块,并对其进行编码,以便即使一部分编码数据不可用,也可以恢复主要数据。横向扩展的分布式存储系统依靠擦除编码来提供数据保护,方法是将编码数据保存在多个驱动器和节点上。如果驱动器或节点发生故障或数据损坏,则可以从保存在其他驱动器和节点上的块中重建原始数据。

擦除编码能够以更高的效率容忍与其他技术相同数量的驱动器故障,方法是将数据跨节点和驱动器进行条带化。存在许多不同的擦除编码算法,并且最大距离可分 (MDS) 代码(如 Reed-Solomon)实现了最高的存储效率。

在对象存储中,要保护的数据单元是对象。一个对象可以存储在 n 个驱动器上。如果 k 表示潜在的故障,则 k < n,并且使用 MDS 代码,系统可以保证容忍 n - k 个驱动器故障,这意味着 k 个驱动器足以访问任何对象。

考虑一个大小为 M 字节的对象,每个编码对象的大小为 M / k(忽略元数据的大小)。与上面显示的 N 路复制相比,当擦除编码配置为 n = 5 和 k = 3 时,分布式存储系统可以容忍 2 个驱动器的丢失,同时将存储效率提高 80%。例如,对于 10 PB 的数据,复制需要超过 30 PB 的存储,而对象存储则需要 15-20 PB 来安全地存储和保护相同的数据,使用擦除编码。擦除编码可以配置为数据与奇偶校验块的不同比率,从而产生一系列存储效率。MinIO 保持一个有用的 擦除代码计算器,以帮助确定您环境中的需求。

MinIO 擦除编码概述

MinIO 使用每个对象的内联擦除编码来保护数据(有关 参考,请参阅 MinIO 官方文档),该编码是用汇编代码编写的,以提供尽可能高的性能。MinIO 利用 Intel AVX512 指令充分利用跨多个节点的主机 CPU 资源,以实现快速擦除编码。标准 CPU、快速 NVMe 驱动器和 100 Gbps 网络支持以接近线速的速度写入擦除编码的对象。

MinIO 使用 Reed-Solomon 代码将对象条带化为数据和奇偶校验块,这些块可以配置为任何所需的冗余级别。这意味着在具有 8 个奇偶校验配置的 16 个驱动器设置中,一个对象将跨 8 个数据块和 8 个奇偶校验块进行条带化。即使您丢失了多达 7 个 ((n/2)–1) 个驱动器(无论是奇偶校验还是数据),您仍然可以从剩余的驱动器可靠地重建数据。MinIO 的实现确保即使多个设备丢失或不可用,也可以读取对象或写入新对象。

MinIO 根据擦除集的大小将对象拆分为数据和奇偶校验块,然后在集中随机且均匀地分布数据和奇偶校验块,以便每个驱动器最多包含每个对象的一个块。虽然一个驱动器可能包含多个对象的(数据和奇偶校验)块,但只要系统中有足够的驱动器,单个对象在每个驱动器上最多只有一个块。对于 版本化对象,MinIO 会为数据和奇偶校验存储选择相同的驱动器,同时保持任何一个驱动器上没有重叠。

下表提供了 MinIO 中擦除编码的示例,其中包含可配置的数据和奇偶校验选项以及相应的存储使用率。

总驱动器数 (n)

数据驱动器数 (d)

奇偶校验驱动器数 (p)

存储使用率

16

8

8

2.00

16

9

7

1.79

16

10

6

1.60

16

11

5

1.45

16

12

4

1.34

16

13

3

1.23

16

14

2

1.14

MinIO 的后端布局实际上非常简单。进入的每个对象都会分配一个擦除集。擦除集基本上是一组驱动器,集群由一个或多个擦除集组成,由总磁盘数量决定。

让我们来看一个简单的示例,以了解 MinIO 中使用的格式和布局。

需要注意的是,格式是关于数据与奇偶校验驱动器比率的——无论我们有四个节点,每个节点只有一个驱动器,还是四个节点,每个节点有 100 个驱动器(MinIO 经常部署在密集的 JBOD 配置中)。

我们可以将四个节点配置为每个节点具有 100 个驱动器,并使用默认的 16 的擦除集大小。这是逻辑布局,它是擦除编码计算定义的一部分。每 16 个驱动器就是一个擦除集,由 8 个数据驱动器和 8 个奇偶校验驱动器组成。在这种情况下,擦除集基于 400 个物理驱动器,平均分配到数据和奇偶校验驱动器,并且可以容忍多达 175 个驱动器的丢失。

使用 MinIO 默认擦除编码配置可以容忍多达一半的驱动器丢失,而不会出现停机或数据丢失。

MinIO 的 XL 元数据与对象一起原子写入,包含与该对象相关的所有信息。MinIO 内部没有其他元数据。影响是巨大的——一切都与对象自包含,使其保持简单且自描述。XL 元数据指示擦除代码算法,例如两个数据与两个奇偶校验、块大小和校验和。将校验和与数据本身一起写入,使 MinIO 能够在支持流数据的同时进行内存优化,这相对于将流数据保存在内存中、然后将其写入磁盘并最终生成 CRC-32 校验和的系统而言,具有明显的优势。

擦除编码的对象作为奇偶校验和数据块跨驱动器进行条带化,并带有自描述的 XL 元数据。

当将一个大型对象(例如,大于 10 MB)写入 MinIO 时,S3 API 会将其分解为多部分上传。客户端在上传时确定部分大小。S3 要求每个部分至少为 5 MB(最后一个部分除外),且不超过 5 GB。根据 S3 规范,一个对象最多可以有 10,000 个部分。想象一个 320 MB 的对象。如果此对象被分成 64 个部分,MinIO 将把这些部分作为 part.1、part.2……一直到 part.64 写入驱动器。这些部分的大小大致相等,例如,作为多部分上传的 320 MB 对象将被分成 64 个 5 MB 的部分。

上传的每个部分都将在条带中进行擦除编码。Part.1 是上传的对象的第一部分,所有部分都横向条带化到驱动器上。每个部分都由其数据块、奇偶校验块和 XL 元数据组成。MinIO 旋转写入,以便系统不会始终将数据写入相同的驱动器,并将奇偶校验写入相同的驱动器。每个对象都独立旋转,允许统一且高效地使用集群中的所有驱动器,同时提高数据保护。

为了检索对象,MinIO 执行哈希计算以确定对象保存的位置,读取哈希并访问所需的擦除集和驱动器。读取对象时,存在 XL 元数据中描述的数据和奇偶校验块。MinIO 中的默认擦除集是 12 个数据和 4 个奇偶校验,这意味着只要 MinIO 可以读取任何 12 个驱动器,就可以提供服务。

擦除编码和 MinIO 实现的优势

与分布式系统中用于数据保护的其他技术相比,擦除编码具有几个关键优势。

擦除编码比 RAID 更适合对象存储的原因有很多。MinIO 擦除编码不仅可以防止在多个驱动器和节点发生故障时数据丢失,还可以保护和修复对象级数据。能够一次修复一个对象是相对于在卷级别修复的 RAID 而言的一个巨大优势。在 MinIO 中可以几秒钟内恢复损坏的对象,而在 RAID 中则需要数小时。如果驱动器出现故障并被更换,MinIO 会识别新的驱动器,将其添加到擦除集中,然后验证所有驱动器上的对象。更重要的是,读取和写入不会相互影响,从而实现大规模性能。有些 MinIO 部署拥有数千亿个对象,跨越 PB 级存储。

MinIO 中的擦除代码实现提高了数据中心的运营效率。与复制不同,无需在驱动器和节点之间进行冗长的重建或重新同步数据。这可能听起来微不足道,但移动/复制对象可能非常昂贵,而 16TB 驱动器发生故障并在数据中心网络中复制到另一个驱动器会给存储系统和网络带来巨大的负担。

如果这篇博文激发了您的好奇心,我们还有更长的 擦除编码入门 可供参考。下载 MinIO 并立即开始使用擦除编码保护数据。