《Operating System:Three Easy Pieces》阅读笔记<三十二>—数据完整性保护

138 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情

数据完整性保护

除了那些文件系统的基本特性,还有一些其它的特性需要研究,现代存储设备越来越快,但是相对的可靠性下降。因此我们需要保证读取数据时返回的数据和我们存入的是一致的,即数据的完整性。如果数据不是完整的,则需要引发故障,磁盘有不同的故障模式,之前我们学到的是fail-stop模式,也就是一遇到故障就立刻停机。这种模式不适用于现代系统,因为现代磁盘的情况是大部分时候大部分的空间都是可用的,只有少部分会有故障。

如果细究的话,常见的现代磁盘故障可以总结为两种,扇区错误(lantent-sector errors)和块损坏(block corruption)。

  • 当一个磁盘扇区出于某种原因物理损坏而无法读取了,这就是LSE,这是可检测的错误,当读取这些位置时,磁盘返回一个错误
  • 磁盘上的数据出现错误则不能够显式的检测出来,当读取它们时,磁盘返回错误的数据,这就是corruption

出现这两类错误的模型被称为fail-partial模型,磁盘看起来是可用的,只是访问某些块时会返回报错或错误数据,事实上,这两类错误发生的概率都不高,有研究表明,在采样150万个磁盘中,出现LSE和Corruption的概率都不高,但是不可忽略,如下表所示。

研究还总结了两类错误的特征,其中

对于LSEs,有:

  • 具有多个LSE的昂贵驱动器与廉价驱动器一样,可能会产生额外的错误
  • 对于大多数磁盘来说,第二年的误差率会逐年上升
  • lse的数量随着磁盘大小的增加而增加
  • 大多数具有lse的磁盘都小于50
  • 具有lse的磁盘更有可能开发额外的lse
  • 存在着大量的时空局部性
  • 磁盘擦洗很有用(大多数lse都是通过这种方式找到的)

对于corruption,有:

  • 在相同的驱动器类中,不同的驱动器模型损坏的机会差别很大
  • 不同型号的老化效应不同
  • 工作负载和磁盘大小对损坏的影响很小
  • 大多数磁盘只发生少量Corruption
  • 在磁盘内或跨磁盘RAID中,Corruption不是独立产生的
  • 既有空间局部性,也有时间局部性
  • 与lse的相关性较弱

我们开始着手处理两种错误,扇区错误更简单处理一些,因为扇区错误更容易检测,当检测到扇区错误时,直接激活磁盘内置的冗余机制返回正确数据即可,如RAID-2和RAID-45所做的那样。

对于corruption,重点在检测,如果知道哪个块错了,就直接采用冗余机制恢复即可。而检测的方法就是我们这次的重点,现代存储系统用来保持数据完整性的主要机制就是校验和,校验和只是一个函数的结果,该函数以一个数据块(比如一个4KB的块)作为输入,并在该数据上计算一个函数,生成一个数据内容的小摘要(比如4或8个字节)。这个摘要称为校验和。这样的计算的目的是使系统能够检测数据是否以某种方式被破坏或改变,方法是将校验和存储在数据中,然后在以后访问时确认数据的当前校验和与原始存储值相匹配。

由许多不同的函数被用来计算校验设,并且强度各不相同,它们在保护数据完整性和计算速度上基本上成反比。最基本的校验和函数是基于异或计算的XOR函数,以16字节的块上计算4字节的校验和为例,4字节的校验和意思就是每4个字节一个校验位。

XOR的优点是简单快速,但是如果有两位发生变化,XOR就失效了,泛用性不足。另一个基本的校验和函数是加法。这种方法的优点是速度快,它可以检测数据中的许多变化,但如果数据发生了移动,它就不太好用了。一个稍微复杂一点的算法被称为弗莱彻校验和(Fletcher check- sum),能够检测所有的单比特、双比特错误和许多突发错误,是一种很强的算法。最后一个常用的校验和称为循环冗余校验(cyclic redundancy check),可以相当有效地实现二进制模运算,因此CRC在网络中的普及程度也很高。

对于一个校验和函数,不同的内容也可能会有相同的校验和结果,这称为collision,一个好的校验和函数应当最小化collision

那么如何在存储系统中使用校验和呢?通常的做法是给每一个扇区或者块一个校验和,校验和的存储如下图所示。

如果磁盘支持小块,那么第一种更好,如果不支持,第二种更合适,但是效率可能会降低。

上面描述的基本方案在损坏块的一般情况下工作得很好。然而,现代磁盘有两种不同寻常的故障模式,需要不同的解决方案。

第一种是错向写,即写入数据正确,但位置错误,这种情况下读取磁盘返回完全不相干的信息,解决方案就是增加一个物理ID,用于标志磁盘和块的位置,每次检验前对照一下,如下图所示。

第二种是写入丢失,磁盘通知系统写入完成后,实际上写入因为某种原因没有成功,这种错误的检验比较复杂,因为之前校验和的方法不能处理这种错误,我们需要额外的机制来处理,关于这一点不同系统的解决的方式有很多种,各不相同,因此这里不讨论。

最后一个点是磁盘擦洗,我们并不是只在数据写入时检查数据正确性,也需要在选择某一个时间统一对整个磁盘进行数据检验,很多时候被安排在每晚或每周,例行检查被称为磁盘擦洗。不同系统也有不同的擦洗规划。

了解了上述的知识后,我们来总结一下校验和的开销,总结来说,校验和的开销有时间开销和空间开销两种。

关于空间开销:

  • 磁盘开销:校验和占磁盘的典型的比率可能是每4 KB数据块需要8字节的校验和,这将导致0.19%的磁盘空间开销。
  • 内存开销:当访问数据时,现在必须在内存中有足够的空间来存放校验和和数据本身。但是,如果系统只是简单地检查校验和,然后在完成校验和之后丢弃它,那么这种开销是短暂的,不太值得关注。

关于时间开销:

  • CPU开销:CPU必须在每个块上计算校验和,无论是在存储数据的时候(确定存储的校验和值),还是在访问数据的时候(再次计算校验和并与存储的校验和进行比较)。许多使用校验和的系统(包括网络栈)都采用了一种减少CPU开销的方法,将数据复制和校验和合并到一个精简的活动中;因为无论如何都需要复制(例如,将数据从内核页面缓存复制到用户缓冲区),因此结合复制/校验和可以非常有效。
  • IO开销:如果校验和完全存储在数据块之外,我们需要额外的IO来访问它们,另外,擦洗所需的IO也是不少的,前者可通过设计减少;可以对后者进行调整,从而限制其影响,或许可以通过控制这种擦洗活动发生的时间来实现