Lec 8 磁盘阵列组织
[RAID: High-performance, Reliable Secondary Storage, 1994](lec9-RAID-High-performance, Reliable Secondary Storage.pdf)
[Disk Failures in the Real World: What Does an MTTF of 1,000,000 Hours Mean to You?](./lec8-Disk Failures in the Real World- What Does an MTTF of 1,000,000 Hours Mean to You?.pdf)
[Flash Reliability in Production: The Expected and the Unexpected, fast16](./lec8-Flash Reliability in Production- The Expected and the Unexpected.pdf)
我们希望磁盘更快、更大、也更可靠,如何做到这样的存储技术?关键技术是什么?有哪些权衡?
本章我们将介绍 RAID(Redundant Array of Inexpensive Disks, 廉价磁盘冗余阵列)。RAID 是一种利用多个磁盘协同工作来构建更快、更大、更可靠磁盘系统的技术。对外部看,一个RAID看起来就像一个普通磁盘,它由一组可以被读取或写入的块组成。但是在内部、RAID是一个相当复杂的系统,它由以下部分组成:
- 多块磁盘
- 内存
- 一个或多个处理器,用来管理整个系统。
因此一个硬件RAID实际上非常像一个计算机、只不过它是专门用来管理一组磁盘的计算机系统。
RAID 的透明性(所谓透明,就是在添加新功能时无需对现有系统做任何修改)促成了它的广泛运用。 比如,系统管理员只需要把原来的 SCSI 磁盘 换成一个 SCSI RAID 存储阵列。
接下来我们会介绍 RAID的接口 和故障模型。 会从三个维度,即容量、可靠性和性能来评估RAID设计。
RAID接口以及内部
当文件系统向 RAID 发出一个逻辑 I/O 请求(logical I/O request)时, RAID 在内部必须计算:
- 应该访问哪一块磁盘(或哪几块磁盘)
- 然后发起一个或多个物理 I/O(physical I/O) 来完成该请求。
这些物理 I/O 的具体形式取决于 RAID 的级别(RAID level)
RAID 系统通常被构建成一个独立的硬件设备,通过标准接口与主机连接,例如:SCSI、SATA。但在内部,RAID 的结构其实相当复杂。
一个 RAID 系统通常包含:
微控制器
运行固件(firmware)
用于控制 RAID 的整体运行
易失性内存
用作缓存
在读写数据块时进行缓冲
非易失性内存
用于安全地缓存写入数据
防止断电丢失数据
在某些 RAID 系统中,还会包含:
- 专门的硬件逻辑,用于执行 奇偶校验(parity)计算 (某些 RAID 级别会用到,我们后面会看到)。
故障模型
为了理解 RAID 并比较不同的设计方案,我们必须先明确一个故障模型(fault model)。RAID 的设计目标是检测并从某些类型的磁盘故障中恢复,因此清楚地知道可能出现哪些故障对于设计一个有效的系统至关重要。
我们首先假设一种非常简单的故障模型,称为 fail-stop 故障模型。
在这种模型中,磁盘只有两种状态:正常工作, 在这种状态下,所有数据块都可以被正常读取或写入。故障, 一旦磁盘发生故障,我们假设它永久性地不可用。
fail-stop 模型的一个关键假设是关于故障检测(fault detection)的。具体来说,当磁盘发生故障时,我们假设这种故障很容易被检测到。例如,在一个 RAID 阵列中,我们假设:RAID 控制器(无论是硬件还是软件), 都能够立即检测到某个磁盘已经发生故障。
因此,在当前讨论中,我们暂时不需要考虑更复杂的“静默故障”(silent failures),例如:磁盘数据损坏(disk corruption),我们也不需要考虑以下情况:某个磁盘整体仍然正常,但其中某一个扇区无法访问,这种情况有时被称为 潜在扇区错误(latent sector error)
如何评估RAID
具体来说,我们会从三个维度来评估每一种 RAID 设计。
容量
关键是:假设我们有N个磁盘,每个磁盘有B个数据块,那么RAID可以为用户提供多少有效容量?
- 如果没有任何冗余,那么容量就是: N * B
- 如果为每个数据块保存两份副本,那么有效容量就是 N * B / 2
可靠性
关键是: 一个RAID设计可以容忍多少个磁盘故障?
性能
在分析 RAID 性能时,可以考虑两个不同的性能指标。
第一个是 单个请求延迟(single-request latency)。 理解 RAID 在处理一个 I/O 请求时的延迟是很有用的,因为这可以揭示在一次逻辑 I/O 操作中能够实现多少并行性。
第二个是 稳态吞吐量(steady-state throughput), 也就是在有大量并发请求时 RAID 阵列能够提供的总带宽。
性能的评估相对比较困难,因为它高度依赖于磁盘阵列所面对的工作负载。因此,在评估性能之前,我们会先介绍一些典型的工作负载类型。
接下来我们将研究三种重要的 RAID 设计:
- RAID Level 0(striping,条带化)
- RAID Level 1(mirroring,镜像)
- RAID Level 4 / RAID Level 5(基于奇偶校验的冗余)
第一个 RAID 级别实际上严格来说并不算真正的 RAID,因为它没有任何冗余。不过,RAID 0在性能和容量方面提供了一个很好的上界,因此仍然非常值得理解。
最简单的条带化方式是:将数据块按照如下方式分布到不同磁盘上(假设有 4 块磁盘)
Disk0 Disk1 Disk2 Disk3
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15基本思想很简单:以轮询(round-robin)的方式把数据块分布到不同磁盘上。这种方法的设计目标是:当系统需要读取连续的数据块(例如大型顺序读取)时,能够最大化利用磁盘之间的并行性。我们把同一行中的数据块称为一个 stripe(条带)。例如 0 1 2 3 就是一个stripe。
上面的例子中,我们做了一个简化的假设,每次只在一块磁盘上放1个数据块(比如4KB),然后再切换到下一块磁盘,实际系统并不是这样的。例如,我们可以这样分布数据,如下图,每个磁盘在切换到下一块磁盘之前,会先写入2个4KB数据

因此,chunk size(块组大小) = 8KB; 一个stripe = 4个chunk = 32KB数据
题外话:RAID 的映射问题(RAID Mapping Problem)
在研究 RAID 的容量、可靠性和性能之前,我们先讨论一个重要问题:映射问题(mapping problem)。这个问题在所有 RAID 系统中都会出现。
简单来说:
当系统要读取或写入某个 逻辑块(logical block) 时,RAID 如何知道应该访问 哪一块物理磁盘以及哪个偏移位置?
对于这些简单的 RAID 级别,这个映射计算其实很简单。
以前面的例子为例:
- chunk size = 1 block = 4KB
- 磁盘数量 = 4
如果逻辑块地址为 A,那么可以通过两个公式计算:
Disk = A % number_of_disks Offset = A / number_of_disks这里都是整数运算。 比如 4 / 3 = 1
条带块大小
chunk size 主要影响 RAID 阵列的性能。
如果 chunk size 很小:
- 单个文件会被分散到很多磁盘
- 读写一个文件时可以并行访问多个磁盘
- 并行度更高
但问题是:访问多个磁盘时需要进行更多磁头定位(positioning), 而整个请求的定位时间取决于 最慢的磁盘定位时间。
如果 chunk size 很大:
- 单个文件可能完全位于一块磁盘
- 文件内部并行性减少
- 需要依赖多个并发请求才能提高吞吐量
但优点是:
- 定位时间更低
如果一个文件完全位于一个 chunk 中, 那么访问它只需要 一块磁盘的定位时间
在接下来的讨论中,我们将假设:
chunk size = 1 block = 4KB回到 RAID-0 的分析, 容量方面,RAID-0 提供的有效容量 = 实际容量,因此利用率是100%;可靠性方面,只要任何一块磁盘数据损坏,整个阵列的数据就会丢失;性能方面,非常好,所有磁盘都被使用,多块磁盘可以并行处理 I/O 请求
为了更详细地理解吞吐量,我们需要先定义一些典型的工作负载。在本节讨论中,我们假设有两种类型的工作负载:
- 顺序工作负载(sequential workload)
- 随机工作负载(random workload)
对于 顺序工作负载,我们假设系统向磁盘阵列发出的请求是连续的大块数据,例如,一次请求访问 1MB 的数据,。对于 随机工作负载,我们假设每个请求的数据量比较小,并且每次请求都访问磁盘上的随机位置,例如一系列的随机请求可能是,访问逻辑地址 10 处的 4KB 数据,然后访问逻辑地址 550000,然后访问逻辑地址 20100;某些重要的工作负载,例如 DBMS 中的事务处理,就会表现出这种访问模式。
为了在分析中体现这种差异,我们假设:在顺序工作负载下,磁盘的数据传输速率为:
S MB/s在随机工作负载下,磁盘的数据传输速率为:
R MB/s通常情况下:
S ≫ R也就是说顺序带宽远远大于随机带宽
当然,现实中的工作负载通常不会这么简单。真实系统中的工作负载往往是:顺序访问、随机访问以及两者之间的各种混合形式
RAID 1
RAID Level 1 是镜像数据,Mirroring。我们为系统的每个数据保存多份副本。当然每一份都应该在不同的磁盘上。
例如:
Disk0 Disk1 Disk2 Disk3
0 0 1 1
2 2 3 3
4 4 5 5
6 6 7 7上面的布局是一种常见方式,通常称为:RAID-10(RAID 1+0)。意思是:先做 镜像(RAID-1), 然后在镜像对之间 条带化(RAID-0),也叫做 stripe of mirrors
另一种常见布局叫:RAID-01(RAID 0+1)。意思是:先做 条带化(RAID-0) 然后再做 镜像(RAID-1)
也叫:mirror of stripes,不过在这里,我们先只讨论上图那种镜像布局。
从镜像读取数据时, RAID有一个选择,他可以从任意一个副本读取数据。例如,如果系统读取逻辑块5,可以从2或3读取。但在写入时,就没有这样的选择,RAID比如同步更新两个副本,否则无法保证可靠性。
分析
容量上,有效利用率为50%;
可靠性角度,RAID-1表现很好,至少可以容忍一个磁盘故障,运气好的话,可以容忍更多,例如上面例子,磁盘0和2同时损坏也不会丢失。
性能角度,先看单个读取请求的延迟,和单个磁盘一样;写入情况稍微不同,需要两个物理写入,平均来看写入延迟会 略高于单个磁盘写入
RAID 一致更新问题
这个问题在所有 多磁盘 RAID 系统 中都会出现。假设:disk0 写入成功、disk1 没有写入,我们希望更新应该是原子的;通常的解决方法是Write-Ahead Log(预写日志),在真正执行写入之前,RAID 先记录:“接下来要更新两个磁盘的数据”。如果系统崩溃:恢复程序可以根据日志重新执行未完成的更新。这样就可以保证:镜像副本始终保持一致。
接下来分析 稳态吞吐量。先看 顺序写入, 每一次逻辑写入都会变成两个,因此写带宽为 (N / 2) * S 也就是最大带宽的一半,顺序读取的性能其实 也是一样。
随机读取 是 RAID-1 的最佳情况。因为读取可以在所有磁盘之间分配。因此随机读带宽为:N * R
RAID 4
现在我们介绍一种不同的为磁盘阵列增加冗余的方法,叫做 Parity(奇偶校验),基于奇偶校验的方案尝试使用 更少的存储容量,从而避免镜像系统所付出的巨大空间代价。
Disk0 Disk1 Disk2 Disk3 Disk4
0 1 2 3 P0
4 5 6 7 P1
8 9 10 11 P2
12 13 14 15 P3这是一个 5 磁盘 RAID-4 系统。对于每一条 stripe(条带) 的数据:我们会增加 一个 parity block(奇偶校验块),用于存储该条带的数据冗余信息。
例如:P1 是由 4、5、6、7 计算得到的奇偶校验值。
XOR 奇偶校验
为了计算 parity,我们需要一个数学函数,使得:
即使 stripe 中丢失任意一个块,也能恢复数据。
一个非常合适的函数就是:
XOR(异或)
XOR 的性质:
- 如果一组 bit 中 1 的数量是偶数 → XOR = 0
- 如果 1 的数量是奇数 → XOR = 1
分析
容量上,每個RAID组需要1个磁盘专门存储parity,因此有效容量是:(N-1) * B
可靠性,可以容忍一个磁盘失效,但是不能容忍2个。
性能分析,
顺序读取可以使用所有数据盘,因此带宽是
(N-1) * S;顺序写入 0 1 2 3,RAID会计算
P0 = XOR(0, 1, 2, 3),然后同时写入Disk0 → 0 Disk1 → 1 Disk2 → 2 Disk3 → 3 Disk4 → P05 个盘 并行写,这是最理想的情况,因此顺序写带宽为
(N-1) * S随机写入,假设要修改block 1,那么P0就不正确了,因此parity也要更新。有两种方法
- 方法1: Additive Parity。读取其他数据块,然后一起做XOR计算,写入Parity盘
- 方法2:Subtractive Parity(常用),读取旧数据Cold,和旧校验码Pold,计算
Pnew = (Cold ⊕ Cnew) ⊕ Pold,写入Cnew和Pnew;一次逻辑写需要4次I/O; 致命问题,就是校验盘容易成为瓶颈,比如两个写请求写 block 4和 block1,数据盘可以并行写,但是校验盘不行。RAID-4 小写入性能非常差。
Parity 盘每个逻辑写需要1个读+1个写,因此吞吐为
R/2读取延迟 ≈ 单盘延迟, 因为只访问一个盘。写入延迟 ≈ 2 × 单盘延迟 (两个读可以并行两个写可以并行)
RAID 5
RAID Level 5, 旋转奇偶校验(Rotating Parity)
为了(至少部分地)解决小写入问题,学者提出了RAID-5, 工作方式几乎与RAID-4完全相同,唯一区别是,奇偶校验块会在所有磁盘之间轮流分布
示例:
Disk0 Disk1 Disk2 Disk3 Disk4
0 1 2 3 P0
5 6 7 P1 4
10 11 P2 8 9
15 P3 12 13 14
P4 16 17 18 19可以看到:
每一条 stripe 的 parity block不再固定在一个磁盘上
而是:在所有磁盘之间轮换分布这样做的目的就是:消除 RAID-4 中 parity disk 的性能瓶颈。
分析
RAID-5 的很多分析结果和 RAID-4 完全相同。例如:
容量,两者相同:(N − 1) × B
可靠性,也相同:最多容忍 1 个磁盘故障
性能上:
顺序读性能,与 RAID-4 相同:
(N − 1) × S顺序写性能也相同:
(N − 1) × S,单次请求延迟 读写延迟都与 RAID-4 相同。随机读取性能,随机读性能 略好于 RAID-4。因为现在 所有磁盘都可以参与读取。
随机写性能,随机写是 RAID-5 相比 RAID-4 最大的改进。例如同时有两个写请求:
write block1
write block10假设:
- block1 的 parity 在 disk4
- block10 的 parity 在 disk2
则会产生:
disk1 + disk4 (block1 + parity)
disk0 + disk2 (block10 + parity)因此:这些 I/O 可以 并行执行。
如果有大量随机写请求:可以让所有磁盘 平均忙碌。在这种情况下:随机写带宽约为:
(N / 4) × R为什么是 1/4?因为每一次逻辑写仍然需要:
2 reads
2 writes总共:4 次 I/O,这就是:Parity RAID 的写入成本。
总结
下表总结了不同 RAID 的能力:
| RAID | 容量 | 可靠性 | 顺序读 | 顺序写 | 随机读 | 随机写 |
|---|---|---|---|---|---|---|
| RAID-0 | N·B | 0 | N·S | N·S | N·R | N·R |
| RAID-1 | (N·B)/2 | 1(确定) | (N/2)·S | (N/2)·S | N·R | (N/2)·R |
| RAID-4 | (N−1)·B | 1 | (N−1)·S | (N−1)·S | (N−1)·R | (1/2)·R |
| RAID-5 | (N−1)·B | 1 | (N−1)·S | (N−1)·S | N·R | (N/4)·R |
延迟:
| 操作 | RAID-0 | RAID-1 | RAID-4 | RAID-5 |
|---|---|---|---|---|
| Read | T | T | T | T |
| Write | T | T | 2T | 2T |
其中:
T = 单个磁盘请求延迟后记
Flash Reliability in Production: The Expected and the Unexpected, fast16 这篇论文 提供了一项 大规模的生产环境实证研究。
我们以前会认为:
- Flash 寿命主要由 P/E cycle(擦写次数)决定
- 写得越多,错误率越高
- SSD 后期才会出现明显错误
- SLC 一定比 MLC 更可靠
数据来自 Google 数据中心生产环境, 这是真实生产环境统计研究,可信度非常高。
规模:
- 6 年数据
- 数百万 drive-days
- 10 种 SSD 型号
- 不同 NAND 技术,包括SLC、MLC、eMLC
监控指标:
- 原始比特错误率(Raw Bit Error Rate(RBER))
- 可纠正错误(Correctable errors)
- 不可纠正错误(Uncorrectable errors)
- 块错误(block failure)
- 芯片错误(chip failure)
- 工作负载(workload)
- 年龄(age)
- 磨损(wear)
SSD 中主要有 3 类错误。
- 原始比特错误,这是 NAND cell 本身读错。原因有charge leakage、read disturb、program disturb、wear,但大多数能被ECC修复
- 可纠正错误:ECC成功修复,系统无感知的,但它是一个非常重要的预警指标
- 不可纠正错误: ECC无法修复;这是就需要RAID,否则就会导致数据丢失
论文最重要的结论:
- 不可纠正错误非常常见。在设备投入使用的前四年,20%–63% 的 SSD 至少会发生一次不可纠正错误。
- 可纠正错误非常普遍,大多数 驱动器运行日(drive days) 都会出现:至少一个可纠正错误。
- RBER 并不能很好预测实际故障,更高的 RBER 并不意味着更高的不可纠正错误发生率。
- UBER 指标也不太有意义,是衡量不可纠正错误的标准指标。
- 错误率随生命周期变化呈 U 型,早期错误较多原因可能是manufacturing defects,后期随着P/E cycle增加,错误上升,但是增长没有想象那么剧烈;PE 循环增长导致错误增加,但增长速度较慢
- 写入量并不是唯一因素;论文发现, 写入量和错误率 相关但不强; 但是受工作时长的影响很大,时间本身也是一个磨损因素
- 错误分布极度不均匀,少数 SSD 产生大部分错误,典型 长尾分布
- SLC 不一定比 MLC 更可靠。
- 相比硬盘,SSD 更少被替换,但用户可见问题更多,例如不可纠正错误。
- 坏块数量具有明显分布特征。 要么非常多,要么非常少。
- 坏块和坏芯片出现率很高,30%–80% 的 SSD 会产生至少一个坏块;2%–7% 的 SSD 会出现至少一个坏芯片。坏芯片映射机制(bad chip remapping)非常重要。