现代存储设备存在不可靠性,文件系统或存储系统必须确保写入的数据安全且未被破坏。这被称为数据完整性 (Data Integrity) 或数据保护 (Data Protection)。
磁盘故障模式
传统的 RAID 系统假设磁盘故障是故障-停止 (Fail-stop) 模式,即磁盘要么完全正常,要么彻底损坏,这种故障易于检测。然而,现代磁盘表现出故障-部分 (Fail-partial) 模式,即磁盘整体看似正常,但部分块无法访问或内容错误。主要包含两种单块故障:
潜在扇区错误
潜在扇区错误 (Latent Sector Errors, LSEs) 是指磁盘扇区受损(如磁头碰撞或宇宙射线导致位翻转),导致位不可读。
磁盘内置的纠错码 (ECC) 会检查块内数据。若数据损坏且 ECC 无法修复,磁盘在收到读取请求时会返回错误。此类故障易于检测(非静默部分故障)。随着磁盘容量增加,LSE 数量增多;存在空间和时间局部性;磁盘清洗 (Disk Scrubbing) 能有效发现 LSE。
块损坏
块损坏 (Block Corruption) 是指磁盘块损坏但磁盘自身无法检测。例如,有缺陷的磁盘固件将块写入错误位置,或数据通过故障总线传输时损坏。
磁盘 ECC 会认为块内容正常,但对客户端而言数据是错误的。这属于静默故障 (Silent Faults),磁盘返回错误数据时无任何异常提示。损坏概率随驱动器型号差异巨大,且存在空间局部性。
处理潜在扇区错误
处理 LSE 相对简单,因为它们容易被检测到。当存储系统访问块并收到磁盘错误时,可利用冗余机制恢复数据:
- 在镜像 RAID 中,访问另一个副本。
- 在基于奇偶校验的 RAID(如 RAID-4/5)中,通过奇偶校验组中的其他块重建数据。
双重故障问题:在 RAID-4/5 中,若发生全盘故障,系统会通过读取其他磁盘来重建数据。如果重建过程中遇到 LSE,重建将无法完成。 解决方案:增加额外的冗余度。例如 NetApp 的 RAID-DP 采用双重奇偶校验。当重建时发现 LSE,额外的奇偶校验块可用于恢复缺失数据。其代价是额外的存储空间开销。
检测损坏与校验和
由于块损坏是静默故障,检测是关键问题。现代存储系统主要使用校验和 (Checksum) 来保证数据完整性。
校验和是一个函数的结果,它将数据块(如 4KB)作为输入,计算出一个简短的摘要(如 4 或 8 字节)。系统将校验和与数据一起存储,在后续访问时,通过比对当前数据的校验和与存储的校验和来判断数据是否被损坏。
常见校验和函数
校验和函数在保护强度和计算速度之间存在权衡:
- 异或 (XOR):对数据块的每个数据块进行 XOR 运算。速度快,但无法检测同一位置的多个位翻转。例如,计算 16 字节数据的 4 字节校验和:
0011 0110 0101 1110 1100 0100 1100 1101 1011 1010 0001 0100 1000 1010 1001 0010 1110 1100 1110 1111 0010 1100 0011 1010 0100 0000 1011 1110 1111 0110 0110 0110 --------------------------------------- 0010 0000 0001 1011 1001 0100 0000 0011 - 加法 (Addition):对数据块进行 2 的补码加法(忽略溢出)。速度快,但对数据移位等错误检测能力差。
- Fletcher 校验和:计算两个校验字节 和 。,。强度接近 CRC,能检测所有单位、双位错误及许多突发错误。
- 循环冗余校验 (CRC):将数据块视为大型二进制数,除以一个约定值 ,余数即为 CRC。可通过二进制模运算高效实现。
由于校验和将大数据映射为小摘要,必然存在冲突 (Collision)(不同数据产生相同校验和)。选择校验和函数旨在最小化冲突概率并保持计算高效。
校验和布局
校验和在磁盘上的存储方式主要有两种:
- 每扇区附加:驱动器格式化为 520 字节扇区,额外的 8 字节用于存储校验和。每次写入只需一次操作。
- 打包存储:在不支持 520 字节扇区的磁盘上,将 个校验和打包存储在一个 512 字节的扇区中,后跟 个数据块。这种方式更新数据时需要读取校验和扇区、更新校验和,再写入校验和扇区和新数据块(一次读,两次写),效率较低。
校验和的使用
读取块 时,客户端同时读取存储的校验和 ,并计算检索到数据的校验和 。
- 若 ,数据大概率未损坏。
- 若 ,检测到数据损坏。
若检测到损坏且系统有冗余副本,则使用副本恢复;若无副本,则只能返回错误。
误导写入问题
误导写入 (Misdirected Write) 是指磁盘或 RAID 控制器将数据正确写入了磁盘,但写到了错误的位置(错误的扇区或错误的磁盘)。此时,目标位置的旧数据未被覆盖,而错误位置的数据被破坏。
解决方案:在校验和中增加物理标识符 (Physical ID)。存储的信息不仅包含校验和 ,还包含该块的磁盘号和扇区偏移量。读取时,客户端检查物理 ID 是否匹配,若不匹配则说明发生了误导写入。
丢失写入问题
丢失写入 (Lost Write) 发生于设备通知上层写入已完成,但实际上数据并未持久化到磁盘,块中保留的仍是旧数据。由于旧数据与其对应的旧校验和及物理 ID 均匹配,上述机制无法检测丢失写入。
解决方案:
- 写入验证 (Write Verify):写入后立即读取数据以确认写入成功。代价是 I/O 数量翻倍,速度极慢。
- 系统级校验和:在系统的其他位置存储校验和。例如,ZFS 在每个文件系统的 inode 和间接块中存储数据块的校验和。即使数据块的写入丢失,inode 中的新校验和也不会与磁盘上的旧数据匹配。只有当 inode 和数据块的写入同时丢失时,该机制才会失效。
磁盘清洗
大部分数据很少被访问,其损坏(位衰减)可能长期未被发现。为了防止所有副本最终都损坏,系统使用磁盘清洗 (Disk Scrubbing)。系统通过定期(如每晚或每周)扫描所有块并验证校验和,及时发现并修复损坏数据,降低数据完全丢失的风险。
校验和的开销
使用校验和会带来空间和时间开销:
- 空间开销:
- 磁盘空间:存储校验和占用磁盘容量(例如 4KB 数据块使用 8 字节校验和,开销约为 0.19%)。
- 内存空间:访问数据时需在内存中存放校验和。通常验证后即丢弃,开销短暂;若保留在内存中以防止内存损坏,则存在持续开销。
- 时间开销:
- CPU 开销:存储和访问数据时均需计算校验和。系统通常将数据拷贝与校验和计算合并为单一操作以降低开销。
- I/O 开销:当校验和与数据分离存储时会增加额外 I/O;后台磁盘清洗也会产生 I/O 负载,通常安排在系统空闲时段进行。