保持数据完整性:受ZFS启发的存储系统

2020-09-04 19:22:34

当我们存储数据时,我们通常假设我们选择的存储系统稍后会按照我们放入的方式返回该数据。但是,我们有什么保证可以证明这是真的呢?这里的情况是位腐烂,即物理上构成当今存储设备的物理电荷的静默降级。

为了解决这类问题,可以使用数据校验和,btrfs和ZFS都是这样做的。然而,虽然从长远来看,btrfs可能是解决这一问题的首选工具,但它相当复杂,而且还不太成熟,而ZFS,这类功能最突出的候选者,并不是没有问题,每次内核更新都必须重新编译它(尽管存在自动化)。

因此,在这篇博客文章中,我们将研究一种存储设计,该设计实际检查返回的数据在我们的存储系统内是否实际有效且没有静默损坏,并且完全使用Linux本身提供的组件进行设计,而无需在每次内核升级时重新编译和测试您的存储层。我们发现,这种存储设计,虽然实现了与ZFS相同的目的,但不仅产生了与ZFS相同的性能,而且实际上在某些情况下甚至能够显著超过它,正如最后的基准测试所表明的那样。

我们将在下面测试的系统将由四个组件(包括文件系统)构成:

DM-INTEGRITY为所有传入数据提供块级校验和,并将检查通过它读取的所有数据的校验和(如果不匹配,则返回读取错误)。

Mdraid可在磁盘运行不正常时提供冗余,从而避免数据丢失。如果DM完整性层检测到来自磁盘的无效数据,则MDRID将看到读取错误,并且可以通过从冗余池中的其它位置读取该错误来立即纠正该错误。

顺序很重要,DM-完整性必须放置在硬盘和MDRID之间,以确保在DM-完整性检测到数据损坏错误之后,MDRID可以纠正数据损坏错误。类似地,DM-CRYPT与冗余无关,因此它应该放在MDRID之上,只需通过一次,而不是多次,因为将其放在DM-INTEGRITY旁边的情况下。2个。

现在我们有了一个组装的系统,我们想要量化不同层的性能影响。为此,测试设置是Kobol Helios4(主要是因为它是此存储系统的预定目标平台),具有四个运行Debian 10“Buster”的磁盘3。Helios4由针对存储系统优化的高能效双核ARM SoC驱动。该设置不是基于整个磁盘构建的,而是基于每个大小为10GiB的分区构建的,允许多个并行设计,因此简化了基准测试过程。

#write:echo 3>;/proc/sys/vm/drop_cache#drop all cachesdd if=/dev/zerof=/path/to/testfile bs=1M count=8000 conv=fdatync#readecho 3>;/proc/sys/vm/drop_cache#drop all cachesdd if=/path/to/testfile of=/dev/zerconv=fdatync。

为了对读取(R)和写入(W)的不同情况进行正确的统计,此过程重复了十次:

我们在数据中看到了不同的有趣结果:首先,Helios4上的加密引擎并不是完全没有影响的,尽管由此产生的性能对于Helios4的指定用途来说仍然绰绰有余。

其次,我们看到添加完整性层确实对写入有显著影响,但对读取的影响可以忽略不计,这表明特别是对于主要打算从添加完整性层读取数据的系统而言,添加完整性层的成本可以忽略不计。

对于写入繁重的系统,性能影响更为显著,但是对于某些工作负载(例如Helios设计用于家庭NAS的情况),性能仍然可以认为是完全足够的,特别是因为通常系统会将这些写入缓存到一定程度,而出于基准测试的目的明确禁用了这一点(请参见基准测试过程中的conv=fdatync)。

写(但不是读)中降级的原因可能是由于完整性层和MDRID层彼此去耦的事实,RAID层没有意识到对于双重奇偶校验设置,它必须有效地写入相同的信息三次,并且完整性层在按照Conv=FDataSync的要求认为数据同步之前必须考虑所有三次写入。

我们发现,这种存储设计的性能指标是产生有用的吞吐速率。下一个问题是关于系统的延迟,为简单起见,我们将仅估计随机4K读取的延迟。此外,如上所述,我们将调查系统不同层的影响。为此,我们在删除所有缓存后,从填满每个配置的存储装载点的18 GB大小的测试文件中随机读取100.000个扇区。

上图产生了几个有趣的见解:首先,我们确实看到缓存命中率接近于0毫秒,此外,我们看到延迟分布在可用范围内相当均匀,最后,也是最有趣的是,我们看到几个层对延迟的影响是可测量的,但对于典型的实际目的来说,这是相当不相关的。

到目前为止,我们已经在其预定的目标平台Helios4上测试了设置。为了将生成的系统与房间中的大象ZFS进行比较,我们将使用不同的测试平台,基于Intel i7-2600K作为CPU和4x1TB磁盘,因为ZFS-DKMS不能可靠地构建在ARM上的Debian Buster上,并且当它实际构建时,它明确声明上游不支持32位处理器(作为Helios的CPU),尽管从技术上讲,系统可以运行。

为了进行更清晰的比较,测试床进行了相应的更改以适应ZFS的偏好。由于ZFS不遵守conv=fdatync 5,因此主内存被限制为1 GB,交换被关闭,测试文件的大小被选择为18 GB。这样,发生的任何缓存都将至少显著减少,因为主内存中操作系统旁边用于缓存的空间很少。

所有测试都在装有Linux 4.19的Debian 10“Buster”上运行,从后端以ZFS-DKMS包0.8.2的形式使用ZFS,两种设置的存储层都采用双奇偶校验(RAID6/raidz2),并且,由于Debian中的ZFS-DKMS包不能进行加密,基于mdraid的设置也是在没有加密层的情况下设置的。

#WRITE:ECHO 3>;/proc/sys/vm/drop_cache#drop all cachesdd if=/dev/Zero of=/path/to/testfile bs=1M count=18000 conv=fDatync#readecho 3>;/proc/sys/vm/drop_cache#drop all cachesdd if=/path/to/testfile of=/dev/Zero conv=fDatync;/proc/sys/vm/drop_cache#drop all cachesdd if=/path/to/testfile of=/dev/零conv=fDatync。

尽管ZFS似乎不遵守任何高速缓存丢弃或同步指令,但它们仍然由基于mdraid的设置执行和遵守。

结果非常有趣:虽然基于md的设置本身的性能不太一致,但它在读取性能上仍然始终优于ZFS,但是在写入方面,ZFS的性能要好得多。6个。

为了调查这种意外平衡的原因,我们注意到,虽然ZFS在一个组件中结合了RAID、完整性和文件系统,但对于基于md的设置,这些是单独的组件。在这些组件中,不仅文件系统,而且dm-Integrity都实现了日志记录,以避免在断电的情况下出现不一致。这会导致增加工作,直到事务完全刷新到磁盘为止,如下图所示,其中测试了基于md的系统(没有加密层),并启用了这两个日志记录。

我们发现写性能显著提高(考虑到提高的因素,它实际上超过了ZFS的写性能)。因此,我们可以得出结论,Linux组件在读性能方面非常有能力与ZFS匹配,至少对于低内存环境是这样,特别是在读性能上优于ZFS。在纯粹的吞吐量优化配置中(例如当安装了不间断电源时,因此日志记录的保证不一定是关键的),它甚至在读和写方面都优于标准的ZFS配置

在基于MD的设置的文件系统层上禁用日志记录对设置的吞吐量性能没有任何可测量的影响。

与Helios4上的延迟测量类似,我们可以测量基于MD的存储和ZFS的延迟配置文件,从18 GB大小的测试文件中读取100万个随机的4K样本。

除了预期的接近零毫秒的缓存命中率之外,我们还发现了一些关于延迟的非常有趣的结果:虽然基于MD的系统与其延迟曲线非常一致,无论是分布还是平均和中位数几乎相同,ZFS表现出稍低的中位延迟,但与高达五倍的较大延迟尾部形成对比,后者将平均延迟显著提高到基于MD的设置的之上,这表明ZFS在读取延迟方面似乎要难以预测得多。在读取延迟方面,ZFS表现出更低的中值延迟,但与高达5倍的延迟尾部形成对比,后者将平均延迟显著提高到基于MD的设置之上,这表明ZFS在读取延迟方面似乎难以预测。

需要注意的一点是,与下面没有完整性层的mdraid相比,建议设置的检查和重新同步显著延长了(3到4倍)。到目前为止,调查尚未揭示根本原因。不受CPU限制,表明读取性能不受校验和的影响,上面的延迟数字并不意味着延迟的显著增加,从而导致检查时间延长3-4倍,禁用日志也不会改变这一点(正如人们所预期的那样,日志不会像日志预期的那样)会导致延迟显著增加,从而导致检查时间延长3-4倍,禁用日志也不会改变这一点(正如人们所预期的那样,日志不会受到校验和的限制),上面的延迟数字并不意味着延迟会显著增加到导致检查时间延长3-4倍,禁用日志也不会改变这一点。

就RAID检查时间而言,ZFS与没有完整性层的mdraid大致相当。

如果有人知道这种行为的根本原因,请联系我,我很想知道。

首先,当考虑是否需要ZFS来保证存储层中的数据完整性时,这一结论可以清楚地用否来回答。不仅mdraid和dm完整性的组合产生的数据速率对于典型的主存储设置来说超过了足够的数据速率,数据还表明,至少在低内存环境中,对于读取密集型操作,这种设置实际上可以超过ZFS,无论是在吞吐量方面,还是在延迟方面,如果不是更可靠的话,至少可以与ZFS相提并论。特别是对于主要针对长期存储解决方案的情况,尤其是对于主要针对读取密集型操作的长期存储解决方案而言。只要写性能足够(数据证实了这一点),这通常是更相关的操作模式。

因此,存储层上的数据完整性是非常有可能的,而不需要在每次内核升级时额外构建ZFS,并且在主存储系统发生故障时保留选项以便于使用任何现代Linux进行恢复(即使ZFS不可用)。

延长的检查和重新同步时间仍然缺乏合理的解释,这是不令人满意的。然而,从纯粹实用的角度来看,重建和重新同步通常是相当罕见的,因此,延长检查和重新同步时间对于保证数据完整性来说是一个相对较小的(且很少支付的)代价。(=。

除了我们可以从我们收集的数据中得出的结论之外,基于MD的设置还具有ZFS(尚未)提供的另一个优点:对于ZFS,存储池的所有磁盘必须在第一组合件9处预先可用(或者对于最终意图具有双重奇偶校验的系统,冗余将不是“任何2个磁盘可能发生故障,并且存储池仍然可操作”),而对于可以扩展或增长池10、将其重新同步到所有(改变的)配置的MDRID则不是这样,在任何时间点(磁盘故障和重建除外)实现“任何两个磁盘都可能出现故障”(对于预期的双奇偶校验/RAID6-设置,随着时间的推移磁盘数量不断增加)。

当然,ZFS不仅仅是块级校验和。ZFS为不同的目的提供了额外的工具,这可能使它的使用仍然有价值。如果要通过其中的一些功能来扩展基于md-RAID的系统,可能会有兴趣使用XFS作为顶层的文件系统,因为在足够新的内核中,XFS还支持文件系统快照等功能。或者,可以在系统中引入LVM层(或者,如果需要,甚至可以用LVM替换md。

这不仅在系统被盗或落入坏人手中时具有数据保护的优势,而且还确保堆栈中的任何磁盘永远不会看到明文数据,因此损坏的驱动器不必擦除(特别是如果它们损坏的方式必须求助于这种擦除)↩︎。

实际上,DM-CRYPT和DM-INTEGRITY的功能可以在同一层内使用。然而,这将是由DM-CRYPT执行的认证加密,其目的是防止篡改,因此使用加密散列。虽然独立的dm-Integrity可以做到这一点,但它在默认情况下使用简单的crc32完整性校验和,这更适合于检测非故意降级。此外,这将意味着不只有一个加密层,而是多个加密层,从而降低性能。实际上,这种设置经过了短暂的测试,在吞吐量方面表现较差。↩︎。

由于DM完整性层执行块级校验和,默认情况下它会覆盖整个磁盘一次,从而确保所有校验和都被正确初始化。然而,这在一个完整的1TB驱动器上需要相当长的时间,大大减慢了创建不同试验床的速度。↩︎。

ZFS声称它能够坚持使用1 Gb/s的旋转磁盘,这大约是下面磁盘在原始字节写入性能上的5倍,这一点很明显。因此,要么ZFS是历史上无与伦比的工程壮举,绕过了硬件限制,要么更有可能的是,尽管被礼貌地要求不要这样做,ZFS仍然在缓存。↩︎。

ZFS性能的某一部分将归因于ZFS执行的剩余缓存,但这应该可以忽略不计。↩︎

当然,如果可以禁用ZFS中的类似组件来调整吞吐速率,这种情况可能会改变,这一点没有进一步研究。↩︎。

启用日志记录后,停电时最糟糕的情况是丢失尚未写入磁盘的数据,但是,设备本身将始终包含校验和一致的块。在完整性层上禁用日志记录意味着在断电的情况下,完整性设备可能最终持有校验和不正确的块。假设这一层位于RAID层之上,只要该块的三个冗余副本中至少有一个包含有效的校验和,理论上RAID层就可以校正那些不一致的校验和。再者,在断电的情况下,a)不能保证将会是这种情况,因为所讨论的文件必须在所有三个冗余副本中被修改,并且b)MD-RAID的标准组装方法将在第一次读取错误时踢出盘,因此分布在不同设备上的多个不一致块理论上可能是可恢复的,但实际上RAID将在遇到第一个错误时踢出那些盘。如果可以修改mdraid行为以更好地适应此用例(没有进一步研究,实际上可能非常简单),则必须对其进行调查。在此之前,我不会禁用冗余层上的日志记录,因为写入性能对于大多数小型设置(如这里介绍的4磁盘设置)来说很可能已经绰绰有余,特别是考虑到在现实生活中,Linux还会在将写入刷新到磁盘之前对它们进行缓存。↩︎。

通过最初构建降级池可以在一定程度上规避这一点,但在添加缺少的磁盘之前,降级池还会降级冗余,这在某种程度上会阻碍此类系统的整体性能。↩︎。

例如,↩︎允许从4磁盘RAID6增长到5磁盘RAID6,或者从3磁盘RAID5迁移到4磁盘RAID6,从4磁盘RAID6迁移到4磁盘RAID5,等等。MDRAID允许从4磁盘RAID6增长到5磁盘RAID6,或者从3磁盘RAID5迁移到4磁盘RAID 6,或者从4磁盘RAID6迁移到4磁盘RAID5,等等