COMP 3150Operating Systems

01 Introduction

Lesson 11.5 presented the disk scheduler and the single device's performance: the seek, the rotational delay, the transfer, and the scheduling algorithms that order the requests. This lesson presents the technique that combines several devices into one logical unit: RAID. The acronym stands for Redundant Array of Inexpensive Disks (or, in the modern usage, Independent Disks), and it was introduced by Patterson, Gibson, and Katz in 1988 in a paper that proposed using several cheap disks in place of one expensive one. The paper's insight was that the several disks could be used not only to improve the storage system's performance (by using them in parallel) but also to improve its reliability (by adding redundancy so that the system survives a disk's failure).

RAID's purpose is therefore twofold, and the two purposes pull in different directions. On the one hand, the several disks can be used in parallel to increase the throughput (the striping technique: the data is spread across the disks, and the requests are served concurrently). On the other hand, the several disks can be used to store redundant information so that the system survives a disk's failure (the mirroring technique: the data is stored on two or more disks, and the system can read from any of them; or the parity technique: an additional piece of data is computed and stored so that a missing piece can be reconstructed). The two techniques can be combined, and the different combinations are the RAID levels.

The RAID levels are the different balances that the designers have chosen. RAID 0 uses the striping alone: it maximizes the performance but provides no redundancy. RAID 1 uses the mirroring: it provides the strongest redundancy at the cost of the storage capacity. RAID 5 uses the striping plus a single parity: it provides the redundancy with a smaller capacity cost, at the price of a more complex write path. RAID 6 uses the striping plus two parities: it survives two disks' failures at the price of a still more complex write path. RAID 10 combines the mirroring and the striping: it provides the strong redundancy and the strong performance at the cost of half the storage capacity.

This lesson presents RAID. It defines the RAID's motivations — the reliability, the availability, and the redundancy — and the storage system's failure model. It presents the three techniques — the striping, the mirroring, and the parity — and the RAID levels that combine them. It presents the RAID levels' performance analysis: the read and write throughput, the small-write problem, and the write-hole. It presents the hardware and software implementations (the RAID controllers, the mdadm, the LVM's RAID targets, the Btrfs's and ZFS's integrated RAID). It presents the rebuild process and its vulnerabilities (the rebuild time, the second failure's risk, the RAID 5 write hole). It presents the RAID's interaction with the solid-state drives (the different failure modes, the write amplification), and it closes with the RAID's design trade-offs and the modern storage systems' choices.

Prerequisites

This lesson builds on Lessons 11.1 through 11.5. The hard disk's performance characteristics (the seek, the rotational delay, and the transfer), the DMA machinery, the device driver and the block layer's request queue, and the disk scheduling algorithms are all assumed. The file system's allocation and its journaling (Lessons 10.5 and 10.6) are the context for the discussion of the RAID's interaction with the file system, and the checksums (Lesson 10.6) are the context for the discussion of the RAID's data integrity. No new operating-system concepts are introduced; the lesson is about the RAID's techniques and their trade-offs. Familiarity with the Linux command line from Lesson 2.2 is helpful, since the interactive activity uses mdadm and the LVM tools.

02 Learning objectives

By the end of this lesson you will be able to

  • Define the reliability, the availability, and the redundancy, and explain the storage system's failure model.
  • Describe the three RAID techniques (striping, mirroring, and parity) and explain how they combine into the RAID levels.
  • Describe the RAID levels (0, 1, 5, 6, 10) and their historical predecessors (2, 3, 4), with their performance, their capacity, and their reliability characteristics.
  • Explain the small-write problem and the write-hole problem, and describe the techniques (the read-modify-write, the reconstruct-write, the battery-backed write cache, the journal) that address them.
  • Compare the hardware and software RAID implementations (the RAID controllers, mdadm, LVM, ZFS, Btrfs).
  • Describe the RAID rebuild process and its vulnerabilities, and explain how the modern storage systems (with SSDs, checksums, and erasure coding) address them.

This lesson contributes to U11.6 and U11.7, and to course learning outcomes CLO1, CLO5, and CLO7.

03 Required reading

  • Operating System Concepts — Chapter 11, Mass-Storage Structure, Section 11.8, RAID Structure. The standard treatment of the RAID levels, their performance, and their reliability.
  • OSTEP — Chapter 38, Redundant Arrays of Inexpensive Disks (RAIDs). OSTEP's treatment of the RAID levels, their evaluation, and their trade-offs is the clearest available account, and it is the basis for the worked example in this lesson.
  • Tanenbaum & Bos — Modern Operating Systems, Chapter 5, Section 5.4.3, RAID. A complementary treatment that develops the RAID levels from first principles and compares them systematically.
  • Patterson, D. A., Gibson, G., & Katz, R. H. — A Case for Redundant Arrays of Inexpensive Disks (RAID). ACM SIGMOD Conference, 1988. The paper that introduced the RAID terminology and the levels 1 through 5.
  • Chen, P. M., Lee, E. K., Gibson, G. A., Katz, R. H., & Patterson, D. A. — RAID: High-Performance, Reliable Secondary Storage. ACM Computing Surveys, 1994. The comprehensive survey of the RAID levels, their performance, and their reliability.
  • Leventhal, A. — Triple-Parity RAID and Reed-Solomon Coding. The extensions of the parity technique beyond the RAID 6's two parities (the erasure codes).
  • Linux man pages: man 8 mdadm, man 4 md, man 7 md, man 8 lvm, man 8 pvcreate, man 8 vgcreate, man 8 lvcreate, man 5 mdadm.conf, man 8 mkfs.ext4, man 8 mkfs.xfs, man 8 mkfs.btrfs, man 8 zpool (if ZFS is installed), man 1 dmesg, man 8 smartctl, man 1 lsblk.

Reading strategy

Read the OSTEP chapter first. It develops the RAID levels from a concrete example — a workload with a sequence of logical block requests — and shows step by step how each level handles a read and a write. The chapter's analysis of the RAID 0, 1, 4, and 5's performance for the sequential and the random workloads is the basis for this lesson's performance discussion, and its discussion of the RAID 5's small-write problem is the basis for the write-hole's discussion. Then read the Silberschatz section for the formal terminology and the comprehensive treatment of the RAID levels 0 through 6. The Patterson et al. paper is the historical source, and it is worth reading for the original motivation and the levels 1 through 5's definitions. The Chen et al. survey is the comprehensive reference, and it is worth consulting for the detailed performance and reliability analysis. The Linux man pages are the practical tools: mdadm is the Linux software RAID's management tool, and the md and mdadm.conf pages document the kernel's RAID device and its configuration. Pay particular attention to the distinction between the reliability (the system's ability to survive a failure) and the availability (the system's ability to continue serving the requests while a failure is being repaired). The two are related but not identical: a system can be reliable (its data is not lost) but unavailable (it cannot serve the requests while the repair is in progress), and the RAID levels' differences are the different balances between the two.

04 Core content

4.1 Reliability, availability, and redundancy

The RAID's motivation is the storage system's failure and the user's need to survive it. The three concepts are related but distinct:

Reliability
The probability that the storage system delivers the correct data over a specified period, without losing or corrupting it. A reliable system's data is preserved across the failures of its components, and the system's design must account for the failure modes — the disks' mechanical failures, the controllers' electronics failures, the cables' disconnections, and the power supply's outages. The reliability's measure is the mean time to data loss (MTTDL), the expected time between the failures that lose the data.
Availability
The fraction of the time that the storage system is able to serve the requests. An available system continues to serve the requests even when a component fails, either by using the redundant components or by degrading gracefully. The availability's measure is the uptime percentage: a system with 99.9% availability is unavailable for about 8.76 hours per year, and a system with 99.999% availability is unavailable for about 5.26 minutes per year. The RAID levels improve the availability by allowing the system to continue serving the requests while a failed disk is being repaired.
Redundancy
The storage system's technique for surviving a component's failure: the additional data (the mirror's copy or the parity's reconstruction information) that the system stores so that it can reconstruct a failed component's data. The redundancy is the mechanism, and the reliability and the availability are the properties that it provides. The redundancy's measure is the redundancy factor: the ratio of the total storage capacity to the usable capacity (a 2-disk mirror has a redundancy factor of 2; a 5-disk RAID 5 has a redundancy factor of 5/4 = 1.25).

The redundancy's design has three questions: what granularity (the disk's failure, the block's failure, or the byte's failure), what technique (the mirroring, the parity, or the erasure coding), and what layout (the data's and the redundancy's placement across the disks). The RAID levels are the different answers, and their differences are the lesson's subject.

4.2 The three RAID techniques

RAID combines three techniques, each with its own characteristics:

Striping
The data is divided into stripes (small units, typically 4 KB to 1 MB), and the stripes are distributed across the disks in a round-robin order. A logical block's physical location is determined by its logical address: block i is on disk i mod N (for N disks), at the appropriate offset. The striping's purpose is the parallelism: the requests that target different disks are served concurrently, so the system's throughput is the sum of the disks' throughputs. The striping's disadvantage is the reliability: a single disk's failure destroys the entire array's data (the failed disk's stripes are lost, and the array has no way to reconstruct them). The striping's granularity is a design parameter: a fine-grained stripe (a few KB) distributes each request across several disks, so a large request's throughput is high but a small request's latency is low; a coarse-grained stripe (a few hundred KB) keeps each request on one disk, so the small requests' latency is low but the large requests' parallelism is reduced.
Mirroring
The data is stored on two or more disks, and the copies are identical. A write updates all the copies; a read can be served by any of the copies (typically the one whose disk is closest, or the one that is least busy). The mirroring's purpose is the reliability: a single disk's failure leaves the other copies intact, and the system can continue serving the requests from the surviving copies. The mirroring's disadvantage is the capacity cost: a 2-disk mirror uses half the raw capacity for the redundancy, so the usable capacity is half the raw capacity. The mirroring's read performance is the sum of the disks' read throughputs (the reads can be distributed across the copies); its write performance is the minimum of the disks' write throughputs (a write must update all the copies, so the write is as slow as the slowest disk). The mirroring's n-way variant (RAID 1 with three or more copies) provides a stronger reliability at a still higher capacity cost.
Parity
The data is striped across the disks, and an additional parity block is computed and stored for each stripe. The parity is the exclusive-or (XOR) of the stripe's data blocks: P = D1 ⊕ D2 ⊕ … ⊕ DN−1. The parity's purpose is the reliability with a smaller capacity cost than the mirroring's: a single parity block can reconstruct a single failed data block, so the array survives a single disk's failure. The reconstruction is straightforward: the failed block Di = P ⊕ D1 ⊕ … ⊕ Di−1 ⊕ Di+1 ⊕ … ⊕ DN−1. The parity's cost is the write amplification: a single data block's update requires the parity block's update as well, and the parity's update requires reading the old data and the old parity (the read-modify-write) or reading the stripe's other data blocks (the reconstruct-write). The parity's design is the source of the RAID 5's small-write problem, which the lesson addresses in section 4.4.

4.3 The RAID levels

The RAID levels combine the three techniques, and each level's design is a different balance. The classical levels (0 through 6) are:

RAID 0 (striping, no redundancy)
The data is striped across N disks with no redundancy. The usable capacity is the sum of the disks' capacities, the read and the write throughputs are N times a single disk's, and the reliability is worse than a single disk's: any disk's failure destroys the entire array's data. RAID 0 is used for the workloads where the performance matters and the data is either replaceable (the scratch space, the build caches) or protected by another mechanism (the application-level replication, the periodic backups).
RAID 1 (mirroring)
The data is stored on two (or more) disks, and the copies are identical. The usable capacity is half the raw capacity (for the 2-disk case), the read throughput is the sum of the disks' (the reads can be distributed), the write throughput is a single disk's (the writes must update both copies), and the reliability is strong: the array survives a single disk's failure. RAID 1 is used for the workloads where the reliability matters and the capacity cost is acceptable (the boot volumes, the database's redo logs, the small high-value datasets).
RAID 2 (bit-level striping with the Hamming code)
A historical level that stripes the data at the bit level and uses the Hamming code for the error correction. RAID 2 is not used in practice (the bit-level striping requires the disks to be synchronized, and the modern disks have their own error correction), and it is mentioned only for completeness.
RAID 3 (byte-level striping with a dedicated parity)
A historical level that stripes the data at the byte level and dedicates a single disk to the parity. RAID 3's design assumes that the requests are large (so the byte-level striping's parallelism is useful) and that the disks are synchronized (so the parity's computation is straightforward). The modern workloads' requests are usually small and the modern disks are not synchronized, so RAID 3 is not used in practice.
RAID 4 (block-level striping with a dedicated parity)
The data is striped at the block level, and a single disk is dedicated to the parity. A read can be served by the data disk that holds the requested block, and a large read can use the parallelism across the data disks. A write requires the parity's update, and the parity disk is the bottleneck: every write to any data disk requires a write to the parity disk, so the parity disk's throughput limits the array's write throughput. The observation is the reason RAID 4 was replaced by RAID 5, which distributes the parity across the disks.
RAID 5 (block-level striping with a distributed parity)
The data is striped at the block level, and the parity is distributed across the disks: for each stripe, one of the disks holds the parity, and the parity's disk rotates from stripe to stripe. The distribution eliminates the RAID 4's parity bottleneck: the writes' parity updates are spread across the disks, so the array's write throughput is higher. The usable capacity is the raw capacity minus one disk's (the parity's overhead: N−1 disks' worth for N disks). The read throughput is N−1 times a single disk's (the data's disks), and the write throughput is reduced by the parity's updates. The reliability is strong for a single disk's failure, and it is the reason RAID 5 is the most common RAID level for the general-purpose storage. The write path's complexity (the read-modify-write or the reconstruct-write) is RAID 5's principal disadvantage, and the RAID 5 write hole (section 4.4) is its most subtle failure mode.
RAID 6 (block-level striping with two parities)
The data is striped at the block level, and two parities are computed and stored for each stripe. The second parity (typically the Reed-Solomon code or the P+Q scheme) allows the array to survive two disks' failures, which is important for the large arrays (the rebuild time for a large array is long, and the second failure's risk is significant during the rebuild). The usable capacity is the raw capacity minus two disks', the write throughput is lower than the RAID 5's (the two parities' updates), and the reliability is the strongest of the classical levels. RAID 6 is used for the large arrays (the storage servers, the backup systems) where the double failure's risk matters.
RAID 10 (a stripe of mirrors)
The disks are grouped into the mirrored pairs (the RAID 1's sets), and the pairs are striped (the RAID 0's arrangement). A write updates both disks of the target pair, and the pairs are used in parallel. The usable capacity is half the raw capacity, the read throughput is the sum of the pairs' (the reads can be distributed across the pairs and within each pair), the write throughput is the sum of the pairs' (the writes to different pairs are concurrent), and the reliability is strong: the array survives a single disk's failure, and (in the common configurations) it can survive several failures as long as no two failures hit the same pair. RAID 10 is used for the workloads where both the performance and the reliability matter (the databases, the transaction-processing systems), and its disadvantage is the capacity cost (half the raw capacity).

The levels' comparison is summarized in the following table. The table's "usable capacity" column assumes N disks of equal size, and the "fault tolerance" column gives the number of the disks' failures that the array survives.

The RAID levels' comparison
Level Technique Usable capacity Read throughput Write throughput Fault tolerance
RAID 0 Striping N disks N× N× 0 failures
RAID 1 Mirroring N/2 disks N× 1× N/2 failures (one per pair)
RAID 4 Striping + dedicated parity N−1 disks N−1× Low (parity bottleneck) 1 failure
RAID 5 Striping + distributed parity N−1 disks N−1× Reduced (parity updates) 1 failure
RAID 6 Striping + two parities N−2 disks N−2× More reduced (two parities) 2 failures
RAID 10 Striping + mirroring N/2 disks N× N/2× N/2 failures (one per pair)
The RAID levels' data layouts A diagram with four panels showing the data layouts for RAID 0, RAID 1, RAID 5, and RAID 10 across four disks. RAID 0 stripes the data blocks (A1, A2, A3, A4) across the disks with no redundancy. RAID 1 mirrors the data blocks (A1, A1, A2, A2) across pairs of disks. RAID 5 stripes the data blocks and distributes a single parity block (P) across the disks, rotating its position from stripe to stripe. RAID 10 combines the mirroring and the striping: the blocks (A1, A2) are striped across the mirrored pairs. THE RAID LEVELS' DATA LAYOUTS RAID 0 — striping Usable: N disks. Fault tolerance: 0. Disk 0 A1 A5 A9 Disk 1 A2 A6 A10 Disk 2 A3 A7 A11 Disk 3 A4 A8 A12 A disk's failure destroys the entire array's data. RAID 1 — mirroring Usable: N/2 disks. Fault tolerance: 1 per pair. Disk 0 A1 A3 A5 Disk 1 (mirror) A1 A3 A5 Disk 2 A2 A4 A6 Disk 3 (mirror) A2 A4 A6 A pair's failure is survivable; the data is read from the mirror. RAID 5 — striping + distributed parity Usable: N−1 disks. Fault tolerance: 1 disk. Disk 0 A1 P2 A5 Disk 1 A2 A3 P3 Disk 2 P1 A4 A6 Disk 3 P0 P4 A7 The parity rotates; one disk's failure is recoverable. RAID 10 — stripe of mirrors Usable: N/2 disks. Fault tolerance: 1 per pair. Disk 0 A1 A3 Disk 1 (mirror) A1 A3 Disk 2 A2 A4 Disk 3 (mirror) A2 A4 The pairs are striped; the performance and the reliability are both strong. Legend data block (A1, A2, …) parity block (P) mirror (identical copies) Each panel shows a four-disk array. The blocks' subscripts denote the logical order.
Figure 11.6 — The RAID levels' data layouts. RAID 0 stripes the data blocks (A1, A2, A3, A4, …) across the disks with no redundancy. RAID 1 mirrors the blocks across the pairs of disks. RAID 5 stripes the data blocks and distributes a single parity block (P) across the disks, rotating its position from stripe to stripe. RAID 10 combines the mirroring and the striping: the blocks are striped across the mirrored pairs, so the performance and the reliability are both strong.

4.4 The small-write problem and the write hole

The RAID 5's parity is the source of its most important performance problem: the small-write problem. When an application updates a single data block in a RAID 5 array, the array must update the corresponding parity block as well, and the parity's update requires the old data and the old parity (or the stripe's other data blocks). There are two strategies:

  • Read-modify-write (RMW). The array reads the old data block and the old parity block, computes the new parity (the old parity XOR the old data XOR the new data), and writes the new data and the new parity. The strategy requires two reads and two writes — four I/O operations for a single logical write. The strategy is efficient when the block's disk is idle, because the reads can be issued in parallel with the writes.
  • Reconstruct-write (RCW). The array reads the stripe's other data blocks, computes the new parity (the XOR of the other data blocks and the new data), and writes the new data and the new parity. The strategy requires N−1 reads and two writes — one read for each of the stripe's other data blocks. The strategy is efficient when the stripe's other blocks are in the cache (or when the write's data is enough to justify the reads), and it is the preferred strategy for the large writes (the writes that span the whole stripe).

The two strategies' cost is the small-write problem: a single logical write requires several physical operations, and the array's write throughput is reduced. The problem is the reason the RAID 5's write throughput is lower than its read throughput, and it is the reason the RAID 5 is not the right choice for the write-heavy workloads (the databases' transaction logs, the write-intensive applications).

The write hole is a subtler problem. Consider a RAID 5 array with the battery-backed write cache (or the disk's own cache) and a power failure in the middle of a stripe's update. The array's data and its parity are inconsistent: the data block is the new value, but the parity block is the old value (or vice versa), and the array's reconstruction of a failed disk's block would be wrong. The problem is the write hole: the window during which the data and the parity are inconsistent, and the window is closed only when the update completes. The write-hole is the reason the RAID 5's reliability is not perfect: a disk's failure during the window (a power failure followed by a disk's failure, or a crash during the rebuild) can corrupt the array's data.

The write-hole's solutions are:

  • The battery-backed write cache. The RAID controller's cache has a battery (or a capacitor), so the cache's contents survive a power failure and the controller can complete the pending updates when the power is restored. The battery-backed cache is the classical solution, and it is the reason the hardware RAID controllers include the battery.
  • The journal (the write-intent log). The array writes the update's intent to a journal (a small log on a separate device or on the array itself) before it applies the update, so a crash's recovery can replay the journal and complete the update. The journal is the software RAID's solution, and it is the reason mdadm's RAID 5 has the --write-journal option.
  • The copy-on-write (the Btrfs's and ZFS's approach). The file system never overwrites a block in place; every modification writes a new block and updates the tree's root pointer atomically. The copy-on-write avoids the write-hole entirely, because the old data and the old parity remain consistent until the new tree is committed. The approach is the modern storage systems' preferred solution, and it is the reason the Btrfs's and ZFS's RAID implementations are more robust than the classical RAID's.

4.5 The RAID's performance analysis

The RAID levels' performance depends on the workload's access pattern. The following analysis assumes N disks with the same performance characteristics, and it gives the read and write throughputs in the units of a single disk's throughput:

The RAID levels' throughput by workload
Level Sequential read Sequential write Random read Random write
RAID 0 N× N× N× N×
RAID 1 N× 1× N× 1×
RAID 5 N−1× N−1× N−1× Low (2 reads + 2 writes)
RAID 6 N−2× N−2× N−2× Lower (more parity's work)
RAID 10 N× N/2× N× N/2×

The table's pattern shows the levels' trade-offs. The RAID 0 is the fastest but the least reliable; the RAID 1 and the RAID 10 are the strongest for the reads but the weakest for the writes (the writes must update all the copies); the RAID 5 and the RAID 6 are the balanced choices for the general-purpose workloads, and their weakness is the random writes' parity's overhead.

The analysis is idealized; the real systems' performance depends on the workload's access pattern, the stripe's size, the cache's size, and the controller's design. The key insight is that the RAID levels' performance is not a single number but a function of the workload's characteristics: a level that is fast for the sequential reads may be slow for the random writes, and the system's design must match the level to the workload.

4.6 Hardware and software RAID

The RAID can be implemented in hardware or in software, and the two implementations have different characteristics:

Hardware RAID
A dedicated controller (a RAID card) performs the RAID's operations: it presents the array to the operating system as a single logical device, and it performs the striping, the mirroring, and the parity's computation in its own hardware. The hardware RAID's advantages are the operating system's simplicity (the OS sees a single device, with no RAID's awareness), the controller's capabilities (the battery-backed cache, the dedicated parity's computation, the hot-swap support), and the performance (the controller's hardware offloads the parity's computation from the host's processor). The hardware RAID's disadvantages are the cost (the controllers are expensive), the vendor lock-in (a failed controller must be replaced with the same model, or the array's metadata must be recovered), and the obsolescence (an old controller's firmware may not be supported by the new operating systems).
Software RAID
The host's processor performs the RAID's operations. The Linux kernel's md (multiple device) driver is the classical implementation, and mdadm is its management tool. The software RAID's advantages are the cost (no additional hardware), the portability (the array's metadata is stored on the member disks, so the array can be moved between the systems), and the flexibility (the array's parameters can be changed at runtime, and the modern kernels support the online resizing and the reshaping). The software RAID's disadvantages are the host's processor's overhead (the parity's computation consumes the processor's cycles, which can be significant on the write-heavy workloads) and the host's memory's overhead (the RAID's metadata and its cache consume the memory). The modern processors' performance has reduced the overhead's significance: a modern multi-core processor can perform the RAID 5's parity's computation at the storage device's full throughput, and the software RAID's performance is close to the hardware RAID's for the general-purpose workloads.

The modern storage systems provide additional implementations:

  • LVM (Logical Volume Manager). The LVM's RAID targets (the raid0, raid1, raid5, raid6, and raid10 targets) provide the RAID's functionality within the LVM's framework. The LVM's RAID is a software implementation that uses the kernel's dm-raid target, and its advantage is the integration with the LVM's other features (the snapshots, the thin provisioning, the online resizing).
  • Btrfs's integrated RAID. The Btrfs file system provides the RAID's functionality as a first-class feature: the administrator specifies the RAID's profile (the single, the raid0, the raid1, the raid10, the raid5, or the raid6), and the file system's allocation policy places the data and the metadata accordingly. The Btrfs's RAID is the copy-on-write's implementation: the data's placement is part of the file system's metadata, and the checksums' verification is the RAID's integrity mechanism. The Btrfs's RAID is more robust than the classical RAID's (the write-hole is avoided by the copy-on-write), and its disadvantage is its maturity (the RAID 5/6 support has had the stability problems).
  • ZFS's integrated RAID. The ZFS file system provides the RAID's functionality through its storage pools: the administrator creates a pool from the devices, and the pool's redundancy is configured with the mirrors or the RAID-Z (the RAID 5-like and the RAID 6-like schemes). The ZFS's RAID is the copy-on-write's implementation, with the checksums and the self-healing: the file system detects the corruption (the checksums) and repairs it from the redundant copy (the mirror or the RAID-Z's parity). The ZFS's RAID is the modern storage systems' most thorough implementation, and its disadvantage is its memory's requirements and its licensing (the CDDL is incompatible with the GPL).
  • The cloud storage's erasure coding. The distributed storage systems (the Ceph, the Swift, the HDFS) use the erasure coding rather than the classical RAID: the data is divided into the fragments, and the additional fragments (the parity-like codes) are computed and distributed across the storage nodes. The erasure coding's advantage is the flexibility: the administrator can choose the number of the data's fragments (k) and the total fragments (n), and the scheme's fault tolerance is n−k fragments' failures. The erasure coding is the cloud storage's standard because it provides the strong reliability at a small capacity cost, and its implementation's complexity is hidden in the storage system's software.

4.7 The RAID's rebuild and its vulnerabilities

When a disk in a RAID array fails, the array must be rebuilt: the failed disk is replaced, and the array reconstructs the failed disk's data from the surviving disks' data and parity (the RAID 5 and the RAID 6) or from the mirror (the RAID 1 and the RAID 10). The rebuild is a critical period for the array's reliability:

  • The rebuild's duration. The rebuild reads the surviving disks' data and writes the reconstructed data to the replacement disk. The rebuild's duration depends on the array's size and the disks' speed: a 4 TB array at 100 MB/s takes about 11 hours, and a 20 TB array takes over 2 days. During the rebuild, the array is vulnerable: the disks are under the heavy load (the rebuild's reads and writes), and the remaining disks' failure would destroy the array's data.
  • The second failure's risk. The mean time to failure (MTTF) of the modern disks is typically 1 to 5 million hours, but the annualized failure rate (AFR) is higher, because the disks' failures are not uniformly distributed: the infant mortality and the wear-out effects make the failures cluster. The bit error rate (the probability that a read returns the wrong data without detecting the error) is typically 10−14 to 10−15, and it is significant for the large arrays: a 12 TB disk's full read is 1013 bits, so the expected number of the undetected errors is about 0.1 to 1 per full read. The error rate's significance is the reason the large arrays use the RAID 6 or the RAID 10 rather than the RAID 5: the second failure's risk during the long rebuild is real.
  • The rebuild's interference. The rebuild's reads and writes compete with the applications' I/O, so the array's performance degrades during the rebuild. The degradation is significant for the RAID 5 and the RAID 6 (the rebuild's reads are the whole array's), and it is smaller for the RAID 1 and the RAID 10 (the rebuild's reads are the surviving mirror's).
  • The silent data corruption. A disk can return the wrong data without reporting an error (the silent data corruption, or the bit rot), and the RAID's parity's reconstruction cannot detect it: if the disk returns the wrong data, the array's reconstruction uses the wrong data, and the corrupted block is propagated to the replacement disk. The silent corruption's defence is the checksums (Lesson 10.6): the file system's or the array's checksums detect the corruption, and (in the RAID 6, the RAID 10, or the RAID-Z) the surviving copies allow the correction. The checksums' necessity is the reason the modern storage systems (ZFS, Btrfs) integrate them with the RAID.

The rebuild's vulnerabilities are the reason the modern storage systems' designs have changed:

  • The RAID 6 and the erasure coding. The RAID 6's two parities allow the array to survive a second disk's failure during the rebuild, and the erasure coding's higher redundancy (n−k fragments) allows the larger arrays to survive more failures.
  • The larger arrays' RAID 10. The RAID 10's mirroring allows the array to survive a disk's failure in each mirror, and the rebuild's duration is shorter (the rebuild reads only the surviving mirror, not the whole array).
  • The checksums and the self-healing. The ZFS's and Btrfs's checksums detect the silent corruption, and their redundant copies allow the correction. The self-healing is the modern storage systems' most important reliability feature, and it is the reason the modern systems' RAID is more robust than the classical RAID.
  • The proactive maintenance. The modern systems' monitoring (the SMART's attributes, the error counters) allows the administrator to replace a disk before it fails, so the array's rebuild is planned and the failure's risk is reduced. The proactive maintenance is the operational defence, and it complements the technical defences.

4.8 RAID and solid-state drives

The solid-state drives' characteristics change the RAID's design in several ways:

  • The failure modes. The SSDs' failures are different from the hard disks': the SSDs tend to fail suddenly (the controller's failure, the firmware's bug, the flash's wear-out), rather than the gradual degradation that the hard disks exhibit. The sudden failures make the proactive maintenance less effective, and they make the array's redundancy more important.
  • The write amplification. The SSDs' flash translation layer performs the read-modify-erase-write cycles, and the RAID's parity's updates add to the amplification: the RAID 5's small write requires the additional reads and writes, and the SSD's internal garbage collection's work is increased. The observation is the reason the SSD-based arrays prefer the RAID 1, the RAID 10, and the RAID-Z (the mirroring's designs) over the RAID 5: the mirroring's writes do not require the parity's read-modify-write, so the amplification is smaller.
  • The rebuild's speed. The SSDs' rebuild is faster than the hard disks' (the SSDs' random reads are nearly as fast as their sequential reads), so the rebuild's window is shorter and the second failure's risk is smaller. The observation is the reason the SSD arrays can use the RAID 5 more safely than the hard disk arrays.
  • The TRIM's propagation. The modern SSDs use the TRIM command to inform the drive which blocks are free, so the drive's garbage collection can reclaim them. The RAID array's TRIM's propagation is a design challenge: the array's logical blocks are the stripes, and the TRIM's information must be translated to the member disks' blocks. The modern Linux kernels support the RAID's TRIM's propagation (the mdadm's --discard option), but the support's performance and correctness vary.

The SSDs' characteristics' implications are the reason the modern storage systems' designs differ for the SSD and the hard disk arrays:

  • The SSD arrays often use the mirroring (the RAID 1, the RAID 10, or the RAID-Z's equivalents) rather than the parity, because the mirroring's writes are simpler and the amplification is smaller.
  • The SSD arrays use the higher-level integrity (the checksums, the end-to-end protection) to detect the silent corruption, and they rely on the redundant copies to correct it.
  • The SSD arrays' controllers (the NVMe devices' internal parallelism, the RAID controllers' SSD-awareness) perform the device-level scheduling and the wear leveling, and the array's design must cooperate with them rather than fight them.

4.9 Common misconceptions

"RAID is a backup."

RAID is not a backup. RAID protects against the disk's failure, but it does not protect against the accidental deletion, the application's bug, the malware, the fire, the flood, or the theft. A RAID array's data can be lost by any of these, and the backup (the separate copy, on the separate medium, at the separate location) is the only defence. The RAID's redundancy and the backup's copy are complementary, and both are necessary.

"RAID 5 is always safe."

The RAID 5's safety depends on the array's size and the disks' characteristics. The large arrays (the tens of terabytes) and the high-capacity disks (the multi-terabyte) make the rebuild's duration long, and the second failure's risk (including the silent corruption's undetected error) is significant. The modern guidance is to use the RAID 6, the RAID 10, or the erasure coding for the large arrays, and to reserve the RAID 5 for the small arrays or the SSD-based arrays (whose rebuild is faster).

"RAID 0 is always faster."

The RAID 0's throughput is the sum of the disks' for the large requests (which the striping's parallelism serves), but its small requests' latency is the minimum of the disks' (a small request is served by one disk, and the array's latency is that disk's). The RAID 0's advantage is the throughput, not the latency, and the RAID 1's or the RAID 10's read latency can be lower (the reads can be distributed, and the least-busy disk can serve the request).

"The hardware RAID is always better than the software RAID."

The hardware and the software RAIDs have different trade-offs. The hardware RAID's advantages are the operating system's simplicity, the battery-backed cache, and the dedicated parity's computation. The software RAID's advantages are the cost, the portability, and the flexibility. The modern processors' performance has reduced the software RAID's overhead's significance, and the modern distributions' software RAID (the mdadm, the LVM's RAID, the Btrfs's and ZFS's integrated RAID) is the appropriate choice for the general-purpose workloads.

"The RAID's parity is a checksum."

The RAID's parity is the XOR of the stripe's data blocks, and it is not a checksum: it does not detect the corruption, it only allows the reconstruction of a missing block. If a disk returns the wrong data (the silent corruption), the RAID's reconstruction propagates the wrong data, and the parity does not detect it. The checksums are the corruption's detection mechanism, and the modern storage systems (ZFS, Btrfs) integrate them with the RAID.

"The RAID's stripe's size does not matter."

The stripe's size determines the parallelism's granularity: a fine-grained stripe (a few KB) distributes each request across the several disks, so the large requests' throughput is higher but the small requests' latency is higher (the request spans the several disks, and the array waits for all of them). A coarse-grained stripe (a few hundred KB) keeps each request on one disk, so the small requests' latency is lower but the large requests' parallelism is reduced. The stripe's size is a tunable parameter, and the array's design must match it to the workload.

"The RAID's rebuild is just a copy."

The rebuild is not a simple copy: for the RAID 5 and the RAID 6, the rebuild computes the failed disk's data from the surviving disks' data and parity, so the rebuild's reads are the whole array's and the reconstruction's computation is significant. The rebuild's duration and its interference with the applications' I/O are the reasons the large arrays' rebuild is a critical period, and the modern systems' designs (the RAID 6, the RAID 10, the erasure coding) are shaped by the rebuild's constraints.

"The RAID's level is a permanent choice."

The RAID's level can be changed (the mdadm's --grow --level= option, the LVM's lvconvert, the Btrfs's and ZFS's balance), but the change is an offline operation (the array's data must be reorganized), and its duration is proportional to the array's size. The level's choice is therefore a consequential decision, and the administrator should make it deliberately, with the array's workload and its growth's expectations in mind.

05 Worked example

The following example traces a RAID 5 array's read and write, computes the throughput and the capacity for a concrete configuration, and shows the rebuild's behaviour and its effect on the array's reliability.

A RAID 5 array's read and write

Consider a RAID 5 array with four disks (the N = 4), each 4 TB, and a stripe's size of 64 KB. The array's usable capacity is (N−1) × 4 TB = 12 TB, and the array presents itself as a single 12 TB block device.

The data's and the parity's placement is the stripe's rotation: for the stripe 0, the disks 0, 1, and 2 hold the data and the disk 3 holds the parity; for the stripe 1, the disks 0, 1, and 3 hold the data and the disk 2 holds the parity; and so on. The rotation distributes the parity's writes across the disks, so the parity's disk is not the bottleneck.

A large sequential read. An application reads 256 KB from the array. The array's stripe's size is 64 KB, so the read spans four stripes. The array's controller (or the kernel's md driver) splits the read into the four stripes' requests, and the requests are issued to the disks concurrently: the stripe 0's data is read from the disks 0, 1, and 2 (and the disk 3's parity is not read, because the read does not need it), the stripe 1's data is read from the disks 0, 1, and 3, and so on. The array's read throughput is the sum of the three data disks' (about 3× a single disk's), and the read's latency is the slowest disk's.

A small random write. An application writes 4 KB to the array. The write targets a single data block in a single stripe, and the array must update the corresponding parity block. The array uses the read-modify-write strategy:

  1. Read the old data. The array reads the 4 KB block from the target disk (the disk that holds the block), so the old data's value is known.
  2. Read the old parity. The array reads the stripe's parity block from the parity's disk.
  3. Compute the new parity. The array computes the new parity = the old parity XOR the old data XOR the new data. The computation is a few XOR operations, and it is fast.
  4. Write the new data and the new parity. The array writes the 4 KB block to the target disk and the new parity to the parity's disk. The two writes are issued concurrently.

The write requires two reads and two writes — four I/O operations for a single logical write. The array's write throughput for the small random writes is therefore about half a single disk's (the four operations' total throughput divided by the four operations' count), and the write's latency is the sum of the operations' latencies. The observation is the small-write problem's concrete cost.

A large sequential write. An application writes 256 KB to the array. The write spans four stripes, and it covers each stripe's data completely. The array uses the reconstruct-write strategy:

  1. Compute the new parity. The array computes the new parity = the new data's blocks XOR (the XOR of the stripe's other data blocks). Because the write covers the whole stripe, the other data blocks are the ones being written, so no reads are needed: the parity is computed from the write's data alone.
  2. Write the new data and the new parity. The array writes the four stripes' data and the four parities to the disks concurrently.

The write requires no reads, and its throughput is about the array's write throughput for the sequential writes (the sum of the data disks' throughputs minus the parity's overhead). The observation is the reason the RAID 5's sequential writes are fast and its random writes are slow: the strategy's choice depends on the write's size relative to the stripe's size.

The array's capacity and its cost

The array's usable capacity is 12 TB, and its raw capacity is 16 TB. The redundancy's cost is 4 TB (25% of the raw capacity), and the array's redundancy factor is 16/12 = 1.33. The comparison with the other levels for the same four disks is:

Level Usable capacity Redundancy cost Fault tolerance
RAID 0 16 TB 0 TB (0%) 0 failures
RAID 1 (2+2) 8 TB 8 TB (50%) 1 failure per pair
RAID 5 12 TB 4 TB (25%) 1 failure
RAID 6 8 TB 8 TB (50%) 2 failures
RAID 10 8 TB 8 TB (50%) 1 failure per pair

The table's pattern shows the RAID 5's capacity's advantage (the 25% cost, versus the 50% cost of the mirroring's designs) and its fault tolerance's limitation (the single failure, versus the RAID 6's two and the RAID 10's per-pair). The RAID 5's balance is the reason it is the most common level for the general-purpose storage, and its limitation is the reason the large arrays use the RAID 6 or the RAID 10.

The rebuild's duration and the second failure's risk

Suppose one of the array's disks fails. The array is degraded (the reads and the writes continue, but the performance is reduced and the array's fault tolerance is zero). The administrator replaces the failed disk, and the array begins the rebuild.

The rebuild reads the surviving disks' data and reconstructs the failed disk's data. For the RAID 5, the reconstruction reads the three surviving disks' data and parity, and it writes the reconstructed data to the replacement disk. The rebuild's I/O's total is the array's data's size (12 TB), and the rebuild's duration at the surviving disks' read throughput (say, 150 MB/s per disk, with the three disks' parallelism) is:

Rebuild time = 12 TB ÷ (3 disks × 150 MB/s)
             = 12 × 10^12 ÷ (450 × 10^6) s
             ≈ 26,667 s
             ≈ 7.4 hours

The rebuild takes about 7.4 hours, and during that window the array has no fault tolerance: a second disk's failure would destroy the array's data. The probability of the second failure depends on the disks' failure rate:

Probability of a second failure in 7.4 hours
  = (7.4 hours ÷ (24 × 365 hours/year)) × AFR
  = (7.4 ÷ 8,760) × AFR
  ≈ 8.4 × 10⁻⁴ × AFR

With an AFR of 2% (0.02), the probability is about 1.7 × 10−5, or about 1 in 59,000. The probability seems small, but it is not negligible for a large data center with thousands of arrays, and it is the reason the RAID 5's use for the large arrays is discouraged. The RAID 6's two parities reduce the probability of the data loss to the probability of the third failure (the second failure during the rebuild is survivable, and the third is the data loss), which is far smaller. The observation is the RAID 6's justification, and it is the reason the large arrays use it.

The silent corruption's probability is a separate concern. A disk's full read of the 4 TB is 3.2 × 1013 bits, and with the bit error rate of 10−14, the expected number of the undetected errors is 0.32 per full read. The probability that a read encounters at least one undetected error is therefore about 1 − e−0.32 ≈ 27%. The probability is significant, and it is the reason the RAID 5's rebuild can fail even without a disk's failure: the rebuild reads the whole array, and if any read encounters an undetected error, the reconstructed data is wrong. The checksums' defence is essential: with the checksums, the silent corruption is detected, and the RAID 6's or the RAID 10's redundant copy allows the correction.

Inspecting a RAID array on a live system

The Linux tools let an administrator inspect a software RAID array's state, its members, and its rebuild's progress.

# Show the array's state
cat /proc/mdstat

# The output shows the array's level (raid5), its
# members (sda1, sdb1, sdc1, sdd1), its state
# ([UUUU] for all members up, [_UUU] for one member
# down), and its rebuild's progress (if any).

# Show the array's details
sudo mdadm --detail /dev/md0

# The output shows the array's level, its chunk size,
# its layout, its members, and its state.

# Show the array's members' SMART status
for disk in sda sdb sdc sdd; do
    echo "=== /dev/$disk ==="
    sudo smartctl -H /dev/$disk 2>/dev/null | tail -2
done

# Show the rebuild's progress
watch -n 5 cat /proc/mdstat

# The rebuild's progress is shown in the mdstat's output
# as the "recovery" or "resync" percentage.

The commands' outputs show the array's state and its members' health. The /proc/mdstat output is the array's live view: the array's level, its members, and its state. The mdadm --detail output is the array's configuration: its chunk size, its layout, and its members. The smartctl output is the members' health: the disks' reallocated sectors, their pending sectors, and their temperature. The three together are the administrator's monitoring tools, and their regular inspection is the proactive maintenance's basis.

06 Interactive activity

Activity · explore RAID on a live system

The goal of this activity is to use the Linux command line to create a software RAID array, to inspect its state, to simulate a disk's failure, and to observe the rebuild. Work through the steps in your course VM. The activity uses the loop devices (the files that the kernel presents as block devices), so it does not require the additional disks.

  1. Create the member devices. Create four loop devices to use as the array's members.
    # Create the member files
    mkdir -p /tmp/raid-demo
    cd /tmp/raid-demo
    for i in 1 2 3 4; do
        dd if=/dev/zero of=disk$i.img bs=1M count=200 2>/dev/null
    done
    
    # Set up the loop devices
    sudo losetup -f 2>/dev/null
    for i in 1 2 3 4; do
        sudo losetup /dev/loop$i /tmp/raid-demo/disk$i.img
    done
    
    # Verify the loop devices
    lsblk | grep loop
    Did the loop devices set up correctly? How large is each? What would the array's usable capacity be for each RAID level?
  2. Create the RAID 5 array. Use mdadm to create a RAID 5 array from the four loop devices.
    # Create the RAID 5 array
    sudo mdadm --create /dev/md0 --level=5 --raid-devices=4 \
        /dev/loop1 /dev/loop2 /dev/loop3 /dev/loop4
    
    # Wait for the initial resync to complete
    watch -n 2 cat /proc/mdstat
    
    # (Wait until the resync's progress reaches 100%,
    # then press Ctrl-C.)
    Did the array create successfully? What is the array's level, its members, and its state? How long did the initial resync take? Why does a RAID 5 array need an initial resync?
  3. Create a file system on the array and test it. Create an ext4 file system on the array, mount it, and write some data.
    # Create the file system
    sudo mkfs.ext4 /dev/md0
    
    # Mount it
    sudo mkdir -p /mnt/raid-test
    sudo mount /dev/md0 /mnt/raid-test
    
    # Write some data
    sudo dd if=/dev/urandom of=/mnt/raid-test/test.bin bs=1M count=50 2>/dev/null
    ls -lh /mnt/raid-test/test.bin
    
    # Compute the file's checksum
    md5sum /mnt/raid-test/test.bin
    Did the file system create successfully? Did the write work? What is the file's checksum? Write down the checksum for the later comparison.
  4. Simulate a disk's failure. Mark one of the array's members as failed, and observe the array's degraded state.
    # Mark a member as failed
    sudo mdadm /dev/md0 --fail /dev/loop1
    
    # Observe the array's state
    cat /proc/mdstat
    sudo mdadm --detail /dev/md0
    
    # Verify the data is still accessible
    md5sum /mnt/raid-test/test.bin
    cat /mnt/raid-test/test.bin > /dev/null && echo "Read OK"
    Is the array degraded? Can the data still be read? Does the checksum match the previous one? What does the [U_UU] (or similar) state mean? Explain the degraded state's performance and reliability implications.
  5. Replace the failed member and rebuild. Remove the failed member, add a new one, and observe the rebuild.
    # Remove the failed member
    sudo mdadm /dev/md0 --remove /dev/loop1
    
    # Create a new member and add it
    dd if=/dev/zero of=/tmp/raid-demo/disk1-new.img bs=1M count=200 2>/dev/null
    sudo losetup /dev/loop5 /tmp/raid-demo/disk1-new.img
    sudo mdadm /dev/md0 --add /dev/loop5
    
    # Observe the rebuild's progress
    watch -n 2 cat /proc/mdstat
    
    # (Wait until the rebuild completes, then press Ctrl-C.)
    Did the rebuild start automatically? How long did it take? What was the array's state during the rebuild? After the rebuild, is the array back to its healthy state?
  6. Verify the data after the rebuild. Verify that the data survived the failure and the rebuild.
    # Verify the data's checksum
    md5sum /mnt/raid-test/test.bin
    
    # Compare with the checksum recorded earlier.
    # They should match, because the data was
    # reconstructed correctly.
    
    # Show the array's final state
    cat /proc/mdstat
    sudo mdadm --detail /dev/md0
    Does the checksum match? What does the result tell you about the RAID 5's reconstruction's correctness? What would have happened if the second disk had failed during the rebuild?
  7. Compare the RAID levels' performance. If you have time, create the arrays of the different levels and compare their write throughputs.
    # (After cleaning up the previous array.)
    # Create a RAID 0 array and measure its write throughput
    sudo mdadm --create /dev/md1 --level=0 --raid-devices=4 \
        /dev/loop1 /dev/loop2 /dev/loop3 /dev/loop4
    sudo mkfs.ext4 /dev/md1
    sudo mount /dev/md1 /mnt/raid-test
    time sudo dd if=/dev/zero of=/mnt/raid-test/write.bin bs=1M count=100 2>&1 | tail -1
    
    # Repeat for the RAID 5, the RAID 6, and the RAID 10.
    # Compare the write throughputs.
    
    # Clean up
    sudo umount /mnt/raid-test
    sudo mdadm --stop /dev/md1
    Which level's write throughput is highest? Which is lowest? Does the result match the lesson's performance analysis? Explain the difference in terms of the small-write problem.
  8. Read the man pages. Read the man pages that document the RAID's management.
    man 8 mdadm
    man 4 md
    man 7 md
    man 5 mdadm.conf
    man 8 lvm
    man 8 lvcreate
    man 8 pvcreate
    man 8 vgcreate
    For each, write a sentence describing its main contribution to your understanding of RAID. The man 8 mdadm page documents the Linux software RAID's management tool; the man 7 md page documents the kernel's multiple-device driver; the man 8 lvcreate page documents the LVM's RAID targets.

Reflect: RAID is one of the storage stack's foundational abstractions, and its design is a study in trade-offs. The RAID levels are the different balances between the performance, the capacity, and the reliability, and the balance's choice depends on the workload's characteristics and the system's requirements. The modern storage systems have moved beyond the classical RAID: the erasure coding, the copy-on-write, and the checksums' integration have made the systems more robust and more flexible. Which of the RAID's techniques matter most for the systems you use? What would you change about the RAID's design if you could? How would you explain the difference between the redundancy and the backup to someone who has never thought about it? What is the right balance between the classical RAID's simplicity and the modern systems' robustness?

07 Knowledge check

Check your understanding

Question 1

Define the reliability, the availability, and the redundancy. How do they relate, and why does the RAID improve all three?

Show answer
The three concepts are related but distinct. Reliability is the probability that the storage system delivers the correct data over a specified period, without losing or corrupting it. A reliable system's data is preserved across the failures of its components, and the reliability's measure is the mean time to data loss (MTTDL). Availability is the fraction of the time that the storage system is able to serve the requests. An available system continues to serve the requests even when a component fails, either by using the redundant components or by degrading gracefully, and the availability's measure is the uptime percentage (99.9%, 99.999%, and so on). Redundancy is the storage system's technique for surviving a component's failure: the additional data (the mirror's copy or the parity's reconstruction information) that the system stores so that it can reconstruct a failed component's data. The redundancy is the mechanism, and the reliability and the availability are the properties that it provides. The three relate as follows: the redundancy provides the system with the ability to survive a failure, which increases the reliability (the data is preserved) and the availability (the system continues to serve the requests). The RAID improves all three by combining the several disks with the redundancy: the striping improves the performance (the parallelism), the mirroring and the parity provide the redundancy, and the redundancy increases the reliability and the availability. The RAID's improvements are not free: the redundancy's cost is the reduced usable capacity, the complexity's cost is the increased management's overhead, and the performance's cost is the write amplification (the parity's updates, the mirror's duplicate writes).

Question 2

Describe the three RAID techniques (striping, mirroring, and parity). What is each technique's purpose, and what are its advantages and its disadvantages?

Show answer
The three techniques are the RAID's building blocks. Striping divides the data into the stripes (small units, typically 4 KB to 1 MB) and distributes them across the disks in a round-robin order. A logical block's physical location is determined by its logical address: block i is on disk i mod N (for N disks). The striping's purpose is the parallelism: the requests that target different disks are served concurrently, so the system's throughput is the sum of the disks' throughputs. Its advantage is the performance (the read and the write throughputs are N times a single disk's for the large requests); its disadvantage is the reliability (a single disk's failure destroys the entire array's data). Mirroring stores the data on two or more disks, and the copies are identical. A write updates all the copies; a read can be served by any of the copies. The mirroring's purpose is the reliability: a single disk's failure leaves the other copies intact, and the system can continue serving the requests from the surviving copies. Its advantages are the strong reliability (a single disk's failure is survivable) and the read performance (the reads can be distributed across the copies); its disadvantage is the capacity cost (a 2-disk mirror uses half the raw capacity for the redundancy) and the write performance (a write must update all the copies, so the write throughput is a single disk's). Parity stripes the data across the disks and computes an additional parity block for each stripe: the parity is the XOR of the stripe's data blocks. Its purpose is the reliability with a smaller capacity cost than the mirroring's: a single parity block can reconstruct a single failed data block, so the array survives a single disk's failure, and the capacity cost is one disk's (versus the mirroring's half). Its advantage is the capacity efficiency; its disadvantages are the write amplification (a single data block's update requires the parity's update, with the read-modify-write or the reconstruct-write) and the write-hole (the window during which the data and the parity are inconsistent, and the crash during the window can corrupt the array).

Question 3

Describe the RAID levels 0, 1, 5, 6, and 10. What is each level's technique, its usable capacity, its performance, and its fault tolerance?

Show answer
The RAID levels combine the three techniques in different balances. RAID 0 uses the striping alone. Its usable capacity is the sum of the disks' (N disks), its read and write throughputs are N times a single disk's, and its fault tolerance is zero (any disk's failure destroys the array's data). It is used for the workloads where the performance matters and the data is replaceable or protected by another mechanism. RAID 1 uses the mirroring. Its usable capacity is half the raw capacity (for the 2-disk case), its read throughput is the sum of the disks' (the reads can be distributed), its write throughput is a single disk's (the writes must update both copies), and its fault tolerance is strong (a single disk's failure is survivable). It is used for the workloads where the reliability matters and the capacity cost is acceptable. RAID 5 uses the striping plus a single distributed parity. Its usable capacity is N−1 disks', its read throughput is N−1 times a single disk's, its write throughput is reduced by the parity's updates (the small-write problem), and its fault tolerance is a single disk's failure. It is the most common level for the general-purpose storage, and its limitation is the rebuild's vulnerability for the large arrays. RAID 6 uses the striping plus two parities. Its usable capacity is N−2 disks', its read throughput is N−2 times a single disk's, its write throughput is lower than the RAID 5's (the two parities' updates), and its fault tolerance is two disks' failures. It is used for the large arrays where the double failure's risk matters. RAID 10 combines the mirroring and the striping: the disks are grouped into the mirrored pairs, and the pairs are striped. Its usable capacity is half the raw capacity, its read throughput is the sum of the pairs' (the reads can be distributed), its write throughput is the sum of the pairs' (the writes to different pairs are concurrent), and its fault tolerance is a single disk's failure per pair (and several failures, as long as no two failures hit the same pair). It is used for the workloads where both the performance and the reliability matter.

Question 4

Explain the small-write problem and the write hole. What are the techniques that address them?

Show answer
The small-write problem is the RAID 5's (and the RAID 6's) performance problem: an application's single data block's update requires the corresponding parity block's update as well, and the parity's update requires the old data and the old parity (the read-modify-write) or the stripe's other data blocks (the reconstruct-write). The read-modify-write strategy reads the old data and the old parity, computes the new parity (the old parity XOR the old data XOR the new data), and writes the new data and the new parity — four I/O operations for a single logical write. The reconstruct-write strategy reads the stripe's other data blocks, computes the new parity (the XOR of the other data blocks and the new data), and writes the new data and the new parity — N−1 reads and two writes. The two strategies' cost is the write amplification: a single logical write requires several physical operations, and the array's write throughput is reduced. The problem is the reason the RAID 5's write throughput is lower than its read throughput, and it is the reason the RAID 5 is not the right choice for the write-heavy workloads. The write hole is a subtler problem: a crash (or a power failure) in the middle of a stripe's update leaves the data and the parity inconsistent, and the array's reconstruction of a failed disk's block would be wrong. The window during which the inconsistency exists is the write hole, and it is closed only when the update completes. The write-hole's solutions are the battery-backed write cache (the controller's cache has a battery, so the cache's contents survive the power failure and the controller can complete the pending updates), the journal (the array writes the update's intent to a small log before it applies the update, so a crash's recovery can replay the journal), and the copy-on-write (the file system never overwrites a block in place, so the old data and the old parity remain consistent until the new tree is committed; the Btrfs and ZFS use this approach). The copy-on-write is the modern storage systems' preferred solution, and it is the reason the Btrfs's and ZFS's RAID implementations are more robust than the classical RAID's.

Question 5

Describe the RAID rebuild's vulnerabilities and the modern storage systems' responses. Why is the RAID 6 preferred for the large arrays?

Show answer
The RAID rebuild is the array's reconstruction of a failed disk's data from the surviving disks' data and parity (the RAID 5 and the RAID 6) or from the mirror (the RAID 1 and the RAID 10). The rebuild is a critical period for the array's reliability, and its vulnerabilities are fourfold. The rebuild's duration: the rebuild reads the surviving disks' data and writes the reconstructed data, and its duration depends on the array's size and the disks' speed (a 12 TB array at the surviving disks' 150 MB/s takes about 7.4 hours). During the rebuild, the array has no fault tolerance: a second disk's failure destroys the array's data. The second failure's risk: the disks' failure rates are not uniformly distributed (the infant mortality and the wear-out effects make the failures cluster), and the large arrays' rebuild's long window makes the second failure's probability significant (the probability of a second failure during a 7.4 hour rebuild at a 2% AFR is about 1.7 × 10−5 per rebuild). The silent data corruption: a disk can return the wrong data without reporting an error (the bit rot), and the RAID's parity's reconstruction cannot detect it — the reconstruction uses the wrong data, and the corrupted block is propagated to the replacement disk. The probability is significant for the large disks (a 4 TB disk's full read at a 10−14 bit error rate has about a 27% chance of encountering at least one undetected error). The rebuild's interference: the rebuild's reads and writes compete with the applications' I/O, so the array's performance degrades during the rebuild. The modern storage systems' responses are: the RAID 6 (the two parities allow the array to survive a second disk's failure during the rebuild, and the third failure is the data loss, which is far less probable), the erasure coding (the n fragments with k data fragments provide the fault tolerance of n−k fragments' failures, which is even stronger), the RAID 10 (the mirroring allows the array to survive a disk's failure in each mirror, and the rebuild's duration is shorter because the rebuild reads only the surviving mirror), the checksums and the self-healing (the ZFS's and Btrfs's checksums detect the silent corruption, and their redundant copies allow the correction), and the proactive maintenance (the SMART's monitoring allows the administrator to replace a disk before it fails, so the array's rebuild is planned). The RAID 6 is preferred for the large arrays because the second failure's risk during the long rebuild is real, and the RAID 6's two parities reduce the data loss's probability to the third failure's probability, which is far smaller. The RAID 6's cost is the additional capacity (two disks' worth instead of one) and the additional write's overhead (the two parities' updates), and the cost is the price of the reliability that the large arrays require.

08 Unit discussion

Discussion prompt

RAID is one of the storage stack's foundational abstractions, and its history is a history of trade-offs. The original RAID paper (Patterson, Gibson, and Katz, 1988) proposed using the several cheap disks in place of one expensive one, and it defined the levels 1 through 5 as the different balances between the performance, the capacity, and the reliability. The levels were the 1980s' answers to the 1980s' constraints: the disks were expensive, the capacity was limited, and the failures were the dominant risk. The levels' designs assumed that the disks' failures were independent and that the rebuild's duration was short, and the levels' trade-offs were the straightforward arithmetic of the redundancy's cost versus its benefit.

The modern storage systems have moved beyond the classical RAID. The disks' capacities have grown (the multi-terabyte disks are common), so the rebuild's duration has grown (the days-long rebuilds are common), and the second failure's risk during the rebuild has become significant. The disks' error rates have not improved as fast as their capacities, so the silent corruption's probability during the rebuild has grown. The modern systems' responses — the RAID 6, the erasure coding, the checksums and the self-healing, the copy-on-write — are the recognition that the classical RAID's assumptions no longer hold, and the systems' designs are the new balances.

The trend is toward integration and end-to-end integrity. The classical RAID was a separate layer below the file system: the RAID controller presented a single device, and the file system was unaware of the array's structure. The modern systems (ZFS, Btrfs) integrate the RAID with the file system: the redundancy is part of the file system's metadata, the checksums detect the corruption, and the self-healing repairs it. The integration's advantages are the stronger integrity (the file system's checksums cover the data, not just the disk's blocks) and the more flexible redundancy (the erasure coding's arbitrary n and k, the per-file's redundancy policies). Its disadvantages are the complexity and the vendor lock-in (the integrated systems' formats are often incompatible).

Where should the redundancy live: in a separate RAID layer, in the file system, or in the application? Should the redundancy's mechanism be the RAID's parity, the erasure coding, or something else? What is the right balance between the classical RAID's simplicity and the modern systems' integrity? Post your response to the Unit 11 discussion forum and reply to at least two classmates. Supports CLO1, CLO5, and CLO7.

09 Practice exercise

This ungraded exercise consolidates the lesson and prepares you for Lesson 11.7. Aim to spend 60–90 minutes on it.

  1. Compare the RAID levels. A storage system has six 4 TB disks. Compute the usable capacity, the redundancy cost, and the fault tolerance for each of the following configurations:
    • RAID 0
    • RAID 1 (three mirrored pairs)
    • RAID 5
    • RAID 6
    • RAID 10 (three mirrored pairs, striped)
    Which configuration offers the best capacity? Which the best reliability? Which the best performance? Which would you choose for a database's primary storage, and why?
  2. Trace a RAID 5 write. A RAID 5 array has four disks and a stripe size of 64 KB. An application writes 8 KB to a logical block that maps to the disk 1, the stripe 2. Trace the write's operations, showing:
    • The old data's read.
    • The old parity's read.
    • The new parity's computation.
    • The new data's and the new parity's writes.
    How many I/O operations does the write require? What is the write amplification? How would the operations differ if the write covered the whole stripe?
  3. Compute the rebuild's probability. A RAID 5 array has eight 8 TB disks. The rebuild reads the seven surviving disks' data at an aggregate 800 MB/s, and the disks' AFR is 1.5%. Compute:
    • The array's usable capacity.
    • The rebuild's duration.
    • The probability of a second disk's failure during the rebuild.
    • The probability of the silent corruption during the rebuild (assume a bit error rate of 10−14).
    Would you use the RAID 5 or the RAID 6 for this array? Justify your answer.
  4. Create and test a software RAID. Use the loop devices and mdadm to create a RAID 5 array, test its behaviour under a disk's failure, and observe the rebuild. (The interactive activity has the detailed steps.)
    # Create the loop devices
    for i in 1 2 3 4; do
        dd if=/dev/zero of=/tmp/raid$i.img bs=1M count=200 2>/dev/null
        sudo losetup /dev/loop$i /tmp/raid$i.img
    done
    
    # Create the RAID 5 array
    sudo mdadm --create /dev/md0 --level=5 --raid-devices=4 \
        /dev/loop1 /dev/loop2 /dev/loop3 /dev/loop4
    
    # Wait for the resync, then create a file system and
    # write some data, then fail a disk and observe the
    # rebuild.
    cat /proc/mdstat
    Record the array's state at each step. Did the data survive the failure? How long did the rebuild take? What would have happened if a second disk had failed during the rebuild?
  5. Compare the RAID levels' throughput. Using the loop devices, create a RAID 0, a RAID 1, a RAID 5, and a RAID 10 array, and measure each array's write throughput for a large sequential write and a small random write.
    # For each level:
    sudo mdadm --create /dev/md0 --level=<level> \
        --raid-devices=<n> /dev/loop*
    sudo mkfs.ext4 /dev/md0
    sudo mount /dev/md0 /mnt/raid-test
    
    # Large sequential write
    time sudo dd if=/dev/zero of=/mnt/raid-test/seq.bin bs=1M count=100 2>&1 | tail -1
    
    # Small random write
    sudo fio --name=randwrite --filename=/mnt/raid-test/rand.bin \
        --size=50M --rw=randwrite --bs=4k --direct=1 \
        --ioengine=libaio --iodepth=4 --runtime=5 2>/dev/null | grep -E 'IOPS|bw'
    
    # Clean up
    sudo umount /mnt/raid-test
    sudo mdadm --stop /dev/md0
    Which level's large sequential write is fastest? Which level's small random write is fastest? Does the result match the lesson's performance analysis? Explain the difference in terms of the small-write problem.
  6. Investigate the write-hole. Research the RAID 5's write-hole and the modern systems' solutions. Write a short paragraph (200–250 words) explaining:
    • What the write-hole is and why it occurs.
    • The classical solutions (the battery-backed cache, the journal).
    • The modern solution (the copy-on-write's approach).
    • The Btrfs's and ZFS's implementations.
  7. Design a storage configuration. A company is building a storage server for its engineering team. The server will hold 100 TB of data, including the source code, the build artifacts, the design documents, and the test results. The data is critical (the company's intellectual property) and the server's availability matters (the team's productivity depends on it). The server has 24 8 TB hard disks and 4 1 TB NVMe SSDs. Design the storage configuration:
    • Which RAID levels would you use for the data's different classes (the source code, the build artifacts, the test results)?
    • How would you use the SSDs (the cache, the metadata, the fast storage)?
    • How would you configure the backups?
    • How would you monitor the disks' health and the arrays' state?
    • How would you handle the disks' failures?
    Justify your design, considering the RAID levels' trade-offs and the modern systems' capabilities.
  8. Reflect. In your own words, explain why the RAID is necessary and describe the three techniques and the main levels. Then describe one situation in your own experience — a system you have administered or a service you have used — where the RAID's design affected the system's reliability or its performance. What was the configuration, and how would a different design have changed the outcome?

10 Summary

  • RAID (Redundant Array of Inexpensive/Independent Disks) combines several devices into one logical unit to improve the storage system's performance and its reliability. Its motivation is the storage system's failure and the user's need to survive it, and the three concepts — the reliability (the data's preservation), the availability (the system's ability to serve the requests), and the redundancy (the mechanism that provides both) — are the design's goals.
  • The three RAID techniques are the striping (the data's division into the stripes and their distribution across the disks, for the parallelism), the mirroring (the data's storage on two or more disks, for the reliability), and the parity (the XOR of the stripe's data blocks, for the reliability with a smaller capacity cost). The RAID levels are the different combinations.
  • The RAID levels are the RAID 0 (the striping alone: the performance without the redundancy), the RAID 1 (the mirroring: the reliability with the half capacity), the RAID 5 (the striping plus a single distributed parity: the balance of the performance, the capacity, and the reliability), the RAID 6 (the striping plus two parities: the double failure's tolerance), and the RAID 10 (the stripe of mirrors: the performance and the reliability with the half capacity). The historical levels (the RAID 2, the RAID 3, the RAID 4) are not used in practice.
  • The small-write problem is the RAID 5's (and the RAID 6's) performance problem: a single data block's update requires the parity's update, and the parity's update requires the read-modify-write or the reconstruct-write. The write hole is the window during which the data and the parity are inconsistent, and a crash during the window can corrupt the array. The write-hole's solutions are the battery-backed write cache, the journal, and the copy-on-write.
  • The RAID can be implemented in hardware (the dedicated controller, with the battery-backed cache and the dedicated parity's computation) or in software (the host's processor, with the md driver, the LVM's RAID targets, the Btrfs's and ZFS's integrated RAID). The two implementations have different trade-offs, and the modern processors' performance has reduced the software RAID's overhead's significance.
  • The rebuild is the array's reconstruction of a failed disk's data. The rebuild's vulnerabilities are its duration (the long window during which the array has no fault tolerance), the second failure's risk, the silent data corruption's probability, and the rebuild's interference with the applications' I/O. The modern systems' responses are the RAID 6, the erasure coding, the RAID 10, the checksums and the self-healing, and the proactive maintenance.
  • The solid-state drives change the RAID's design: the SSDs' sudden failures, the write amplification's significance, the faster rebuilds, and the TRIM's propagation. The SSD arrays often use the mirroring (the RAID 1, the RAID 10, the RAID-Z) rather than the parity, because the mirroring's writes are simpler and the amplification is smaller.
  • The modern storage systems have moved beyond the classical RAID: the erasure coding (the n fragments with k data fragments) provides the stronger reliability at a smaller capacity cost, the copy-on-write avoids the write-hole, and the checksums' integration with the RAID provides the end-to-end integrity and the self-healing. The ZFS's and Btrfs's integrated RAID are the modern systems' most thorough implementations.

Looking ahead

This lesson presented RAID: the reliability, the availability, and the redundancy, the three techniques (the striping, the mirroring, and the parity), the RAID levels and their trade-offs, the small-write problem and the write hole, the hardware and software implementations, the rebuild's vulnerabilities and the modern systems' responses, and the RAID's interaction with the solid-state drives. It referred several times to the solid-state drives' characteristics — the flash memory's organization, the flash translation layer, the wear leveling, the garbage collection, and the internal parallelism — and to their effect on the storage stack's design. Lesson 11.7 presents the SSD architecture in detail: the flash memory's physics, the NAND and the NOR technologies, the pages and the erase blocks, the flash translation layer, the wear leveling, the garbage collection, the write amplification, the TRIM command, and the SSD's internal parallelism. The lesson completes Unit 11's view of the storage stack, and it is the foundation for the flash-specific file systems (Lesson 10.7's F2FS) and the modern storage systems' designs.

11 References

  • Silberschatz, A., Galvin, P. B., & Gagne, G. Operating System Concepts, 10th ed., Chapter 11, Section 11.8.
  • Arpaci-Dusseau, R. H., & Arpaci-Dusseau, A. C. Operating Systems: Three Easy Pieces, Chapter 38, Redundant Arrays of Inexpensive Disks (RAIDs). Available online at ostep.org.
  • Tanenbaum, A. S., & Bos, H. Modern Operating Systems, 4th ed., Chapter 5, Section 5.4.3.
  • Patterson, D. A., Gibson, G., & Katz, R. H. A Case for Redundant Arrays of Inexpensive Disks (RAID). ACM SIGMOD Conference, 1988.
  • Chen, P. M., Lee, E. K., Gibson, G. A., Katz, R. H., & Patterson, D. A. RAID: High-Performance, Reliable Secondary Storage. ACM Computing Surveys, 1994.
  • Rodeh, O., Bacik, J., & Mason, C. BTRFS: The Linux B-Tree Filesystem. ACM Transactions on Storage, 2013.
  • Bonwick, J., & Moore, B. ZFS: The Last Word in File Systems. Sun Microsystems, 2007.
  • Linux man-pages: man 8 mdadm, man 4 md, man 7 md, man 5 mdadm.conf, man 8 lvm, man 8 pvcreate, man 8 vgcreate, man 8 lvcreate, man 8 mkfs.ext4, man 8 mkfs.xfs, man 8 mkfs.btrfs, man 8 smartctl, man 1 lsblk.
👤