RAID and Redundant Storage

A single disk drive fails eventually — often when you least expect it. Modern datacenters run thousands of drives. If each has even a small annual failure rate, hardware failures become a daily occurrence rather than a rare event. The engineering question is not whether a disk will die, but how to keep serving correct data when it does. This must be done without paying for a fully duplicated storage system per byte stored. RAID — a Redundant Array of Independent Disks — answers this by spreading data across multiple physical drives. It does so according to a scheme that trades off capacity, performance, and fault tolerance. Some RAID levels chase raw speed by splitting data into pieces. These pieces are read and written in parallel. Others sacrifice capacity to keep exact copies of data. Still others use error-correcting parity to survive drive failures. This approach pays a much smaller capacity tax than full duplication. Understanding these trade-offs is essential background for anyone building or reasoning about file systems, databases, or cloud storage backends. RAID sits directly beneath the Allocation Methods for File Storage layer. It determines what "a disk" even means to the file system above it. This article builds RAID up from first principles. These include the physical disk model from Disk Structure and Geometry, the two basic techniques of striping and mirroring, and the mathematics of parity. Parity lets a system reconstruct lost data from survivors. We derive, rather than just state, why RAID 5 can lose at most one disk. We also show why RAID 6 can lose two. We explain why RAID 5 has a notorious "write penalty." Finally, we show why rebuild time — not just redundancy level — increasingly dominates real-world reliability calculations as drives get larger. By the end, you will be able to compute the usable capacity, read/write cost, and fault tolerance of any standard RAID level from its definition. You will be able to trace through an actual parity reconstruction by hand. You will also be able to reason about why enterprise systems increasingly favor RAID 6 or RAID 10 over plain RAID 5. This groundwork also sets up the reasoning needed later for Protection and Access Control, where redundancy interacts with questions of who can read or corrupt shared data.

Key takeaways
  • RAID combines multiple physical disks into one logical volume to improve performance, capacity, and/or fault tolerance.
  • Striping (RAID 0) splits data across disks for parallel I/O but adds zero redundancy — one failure loses everything.
  • Mirroring (RAID 1) duplicates data fully; it tolerates disk loss at the cost of using only half the raw capacity.
  • Parity schemes (RAID 4/5/6) use XOR-based redundancy to tolerate one (RAID 4/5) or two (RAID 6) disk failures with much less capacity overhead than mirroring.
  • RAID 5 suffers a write penalty: every small write costs 4 I/Os (read-old-data, read-old-parity, write-new-data, write-new-parity).
  • As drives grow, rebuild time grows too, making RAID 5's single-fault tolerance increasingly risky — a major reason RAID 6 and RAID 10 dominate modern deployments.
The core insight

RAID converts many unreliable, independent disks into one logical disk with a chosen point on the capacity–performance–reliability trade-off curve, using striping for parallelism, mirroring for full duplication, and XOR parity for cheap fault tolerance.

The idea

A RAID array presents N physical disks to the operating system as one (or a few) logical volumes. The controller — implemented in hardware, firmware, or a software layer inside the OS — decides how to lay data across the physical disks and, in redundant levels, how to compute and store extra recovery information. From the file system's point of view, sitting atop the abstractions of Disk Structure and Geometry, a RAID volume just looks like a bigger, faster, or more durable disk; the striping, mirroring, and parity machinery is invisible above the block-device interface. Three primitive techniques combine to form the standard RAID levels. Striping divides a logical block address space into fixed-size chunks called stripe units, distributing consecutive stripe units round-robin across disks so that a large sequential access touches many disks in parallel — this is pure performance engineering with no redundancy. Mirroring stores an identical copy of every block on two (or more) disks, so a request can be served by either copy and a disk's failure loses no data, at the cost of doubling (or more) the physical capacity needed. Parity computes a checksum block — the bitwise XOR of corresponding blocks across a set of data disks — and stores it on a dedicated or rotating parity disk, so that if any single disk in the set fails, its content can be reconstructed by XOR-ing the survivors together. The key mathematical fact underlying parity RAID is that XOR is its own inverse: for blocks D1,…,DkD_1, \dots, D_k with parity P=D1⊕D2⊕⋯⊕DkP = D_1 \oplus D_2 \oplus \cdots \oplus D_k, losing any single DiD_i can be recovered as

Di=P⊕D1⊕⋯⊕Di−1⊕Di+1⊕⋯⊕Dk,D_i = P \oplus D_1 \oplus \cdots \oplus D_{i-1} \oplus D_{i+1} \oplus \cdots \oplus D_k,

because XOR-ing a value with itself cancels it (`x ⊕ x = 0`) and XOR-ing with 0 is the identity. This single identity is the entire engine behind RAID 4, RAID 5, and (with a second, independent parity function) RAID 6.

How it works

RAID 0 — Striping, no redundancy

Data is divided into stripe units of size S and written round-robin across N disks. Logical block b maps to disk `b mod N`, offset `b div N` (in units of S). There is no parity and no duplication, so RAID 0 maximizes usable capacity (NN disks of capacity CC give N⋅CN \cdot C usable) and throughput (up to N×N\times parallel bandwidth on large sequential transfers), but any single disk failure destroys the entire array, since every stripe unit is unique.

RAID 1 — Mirroring

Every logical block is written identically to two physical disks. Usable capacity is CC (half of 2C2C raw), but the array survives the loss of either disk in a mirrored pair, and reads can be load-balanced across both copies for roughly 2×2\times read throughput. Many systems generalize this to NN-way mirroring or combine it with striping (RAID 10 / RAID 01) to get both parallelism and redundancy.

RAID 4 — Dedicated parity disk

NN disks hold data, one additional disk holds parity, where stripe ii's parity block is the XOR of the data blocks in stripe ii across all NN data disks. Usable capacity is N⋅CN \cdot C out of (N+1)⋅C(N+1)\cdot C raw. The dedicated parity disk becomes a bottleneck: every write to any data disk also requires an update to the same parity disk, serializing all writes through one drive.

RAID 5 — Rotating parity

RAID 5 fixes RAID 4's bottleneck by rotating which physical disk holds the parity block, stripe by stripe, so parity load is spread evenly across all N+1N+1 disks. Usable capacity is identical to RAID 4: N⋅CN \cdot C out of (N+1)⋅C(N+1)\cdot C raw, tolerating exactly one disk failure.

RAID 6 — Double parity

RAID 6 stores two independent parity blocks per stripe (commonly one XOR parity and one Reed–Solomon-style "Q" parity over a Galois field), tolerating any two simultaneous disk failures. Usable capacity is N⋅CN \cdot C out of (N+2)⋅C(N+2)\cdot C raw. The following pseudocode captures the essential read/write/rebuild logic shared by parity-based RAID (4/5); RAID 6 is the natural extension with a second parity function. ``` // N data disks (0..N-1) indexed logically; parity disk index rotates per stripe for RAID 5, // is fixed (disk N) for RAID 4. function PARITY_DISK(stripe_index): if scheme == RAID4: return N // always the same physical disk else: // RAID5 return stripe_index mod (N + 1) // rotates function WRITE_BLOCK(stripe_index, data_disk, new_data): p_disk = PARITY_DISK(stripe_index) old_data = READ(data_disk, stripe_index) old_parity = READ(p_disk, stripe_index) new_parity = old_parity XOR old_data XOR new_data // update parity incrementally WRITE(data_disk, stripe_index, new_data) WRITE(p_disk, stripe_index, new_parity) // total I/O: 2 reads + 2 writes = 4 disk operations (the "RAID-5 write penalty") function READ_BLOCK(stripe_index, data_disk): if disk_is_healthy(data_disk): return READ(data_disk, stripe_index) else: // reconstruct from all surviving disks in the stripe (data + parity) result = 0 for each disk d in stripe_index's disk set, d != data_disk: result = result XOR READ(d, stripe_index) return result function REBUILD_FAILED_DISK(failed_disk, spare_disk): for stripe_index = 0 to LAST_STRIPE: recovered = READ_BLOCK(stripe_index, failed_disk) // uses XOR reconstruction path WRITE(spare_disk, stripe_index, recovered) mark spare_disk as healthy // cost: one full sequential read of every surviving disk, one full write of the spare ``` The small-write penalty is the crux of RAID 5's performance story: a single logical block write costs four physical I/Os (read old data, read old parity, write new data, write new parity), rather than the one I/O a plain disk write would cost. Large, full-stripe writes avoid this by computing parity directly from the new data across the whole stripe rather than reading old contents first — an optimization called a full-stripe write.

Figure
RAID levels compared: striping, mirroring, and parity layouts across four disks
RAID 0 — striping (no redundancy) A1 A2 A3 A4 disks: D0 D1 D2 D3 (stripe unit each; no fault tolerance) RAID 1 — mirroring A A (copy) B B (copy) 50% usable capacity; survives loss of one disk per mirrored pair RAID 5 — rotating parity (N=3 data + 1 parity) D0 D1 D2 P0 D3 D4 P1 D5 P2 D6 D7 D8 parity block (red) rotates disk each stripe row → no single write bottleneck RAID 6 — double rotating parity D0 D1 P0 Q0 D2 P1 Q1 D3 tolerates ANY two disk failures per stripe (XOR parity + Galois-field parity) Usable capacity (N data disks, C each) RAID 0: N·C (100%) RAID 1: C (50%, 2-way mirror) RAID 5: N·C of (N+1)·C raw RAID 6: N·C of (N+2)·C raw Fault tolerance: RAID0 = 0 disks · RAID1 = 1 disk (per pair) · RAID5 = 1 disk (any) · RAID6 = 2 disks (any) Small-write cost: RAID0/1 = 1–2 I/Os · RAID5 = 4 I/Os (read-old×2, write-new×2) · RAID6 = 6 I/Os

Complexity & correctness

Capacity. With NN data disks of capacity CC each:

  • RAID 0: usable =NC= N C (no overhead).
  • RAID 1 (2-way mirror over NN pairs): usable =NC= N C out of 2NC2NC raw, i.e., 50% efficiency independent of NN.
  • RAID 4/5: usable =NC= N C out of (N+1)C(N+1)C raw; efficiency →100%\to 100\% as N→∞N \to \infty, but fault tolerance stays fixed at one disk.
  • RAID 6: usable =NC= N C out of (N+2)C(N+2)C raw; same asymptotic trend, tolerating two disk failures.

Throughput. For large sequential I/O, striping across NN (or N+kN{+}k) disks yields up to N×N\times (or (N+k)×(N{+}k)\times) the single-disk bandwidth, since independent disks transfer in parallel — this is the entire motivation for RAID 0 and a major side benefit of RAID 4/5/6/10. For small random writes, the picture differs sharply:

  • RAID 0/1: one write costs 1 I/O (RAID 0) or 2 I/Os (mirrored write to both copies, RAID 1), each parallelizable, so latency stays near single-disk latency.
  • RAID 4/5: a partial-stripe write costs 4 physical I/Os — read old data, read old parity, write new data, write new parity — because parity must be recomputed incrementally via
Pnew=Pold⊕Dold⊕Dnew.P_{\text{new}} = P_{\text{old}} \oplus D_{\text{old}} \oplus D_{\text{new}}.

This is the small-write penalty, and it is the dominant reason parity RAID underperforms mirroring on write-heavy random workloads such as OLTP databases.

  • RAID 6: analogous reasoning gives 6 physical I/Os per small write (two parities to read and rewrite instead of one).
  • A full-stripe write (all NN data blocks of a stripe overwritten together) avoids reading old data entirely: parity is computed directly as the XOR of the NN new blocks, costing N+1N + 1 (or N+2N+2) I/Os for a whole stripe rather than 4N4N (or 6N6N) — a large win for sequential or batched workloads, which is why log-structured and copy-on-write file systems pair well with parity RAID.

Correctness of parity reconstruction. The claim to verify: given NN data blocks D1,…,DND_1,\dots,D_N and parity P=D1⊕⋯⊕DNP = D_1 \oplus \cdots \oplus D_N, if any single DiD_i is lost, it is uniquely recoverable from the rest plus PP. Proof. XOR over bit vectors of fixed length forms an abelian group where every element is its own inverse (x⊕x=0x \oplus x = 0) and 00 is the identity (x⊕0=xx \oplus 0 = x). XOR-ing PP with all surviving blocks:

P⊕⨁j≠iDj=(⨁k=1NDk)⊕⨁j≠iDj=Di⊕⨁j≠i(Dj⊕Dj)=Di⊕0=Di.P \oplus \bigoplus_{j \neq i} D_j = \Big(\bigoplus_{k=1}^{N} D_k\Big) \oplus \bigoplus_{j \neq i} D_j = D_i \oplus \bigoplus_{j \neq i} (D_j \oplus D_j) = D_i \oplus 0 = D_i.

Each DjD_j for j≠ij \neq i appears exactly twice (once from PP's definition, once from the explicit XOR) and cancels, leaving exactly DiD_i. This holds for any single missing operand, which is precisely why RAID 4/5 tolerate exactly one failed disk per stripe — losing two disks removes two distinct unknowns from one equation, which is underdetermined and unrecoverable with single parity.

Why RAID 6 tolerates two failures. RAID 6 adds a second, algebraically independent parity function QQ (typically a weighted sum in a Galois field GF(28)GF(2^8), using distinct nonzero coefficients per disk). With two independent linear equations,

P=⨁iDi,Q=⨁igi⋅Di(modfield),P = \bigoplus_i D_i, \qquad Q = \bigoplus_i g_i \cdot D_i \pmod{\text{field}},

losing any two blocks Di,DjD_i, D_j leaves a 2×22\times2 linear system in the two unknowns that is solvable exactly when the coefficient matrix is invertible — which the Reed–Solomon-style coefficient choice guarantees for all disk-index pairs. This is the same reasoning as [Reed–Solomon error-correcting codes]: two independent syndromes let you localize and solve for two erasures.

Rebuild time and the reliability trap. Rebuilding a failed disk in RAID 5 requires reading every surviving disk in full to reconstruct the lost data via the parity identity, then writing the spare. Rebuild time scales with disk capacity and is bounded below by sequential-read bandwidth: for a TT-terabyte drive at sustained rate RR MB/s, rebuild takes roughly T/RT / R (hours, for modern multi-terabyte drives). During this window, the array runs in a degraded state with zero remaining redundancy (RAID 5) — a second failure or even an unrecoverable read error on any surviving disk during rebuild causes data loss. As drive capacities have grown far faster than sustained transfer rates, rebuild windows have stretched from minutes to many hours, making this a first-order reliability concern, not a footnote — the practical reason RAID 6 (tolerating a failure during rebuild) and RAID 10 (rebuild is a fast single-disk copy, not an NN-disk XOR scan) have displaced plain RAID 5 in most modern deployments.

Honesty about trade-offs. RAID is not a backup: it protects against disk hardware failure, not against accidental deletion, file system corruption, ransomware, or a controller bug that writes bad data to all disks simultaneously. RAID also does not protect against silent data corruption (bit rot) unless paired with checksumming file systems (e.g., ZFS, Btrfs) that can detect which copy is corrupt — plain RAID 5/6 parity alone can detect a mismatch but cannot always tell which of N+1N{+}1 disks is the culprit without an extra checksum per block.

Worked example

Consider a RAID 5 array with N=3N = 3 data disks plus 1 rotating parity disk, storing one stripe of 8-bit blocks:

Disk 0Disk 1Disk 2Disk 3 (parity, this stripe)
`D0 = 1101 0011``D1 = 0110 1010``D2 = 1011 0100``P = D0⊕D1⊕D2`

Step 1 — compute parity at write time: ``` D0 1101 0011 D1 XOR 0110 1010 -------------------- = 1011 1001 D2 XOR 1011 0100 -------------------- P = 0000 1101 ``` So parity `P = 0000 1101` is stored on Disk 3 for this stripe. Step 2 — Disk 1 fails. The array is now degraded; any read targeting Disk 1's blocks must reconstruct on the fly using `READ_BLOCK` from the pseudocode above: ``` recovered = D0 XOR D2 XOR P ``` Compute: ``` D0 1101 0011 D2 XOR 1011 0100 -------------------- = 0110 0111 P XOR 0000 1101 -------------------- recovered = 0110 1010 ``` This matches `D1 = 0110 1010` exactly — the reconstruction is correct, confirming the group-cancellation argument from the correctness section with real bits. Step 3 — a write arrives for `D0` while Disk 1 is still failed, changing `D0` to `1111 0000`. In a healthy array this would cost 4 I/Os (read old `D0`, read old `P`, write new `D0`, write new `P`); in degraded mode the controller must instead reconstruct the current logical value of every block in the stripe from survivors, apply the update, and recompute parity directly from all logical values — a strictly more expensive degraded write, illustrating why performance craters further during rebuild, on top of the lost redundancy. Step 4 — rebuild. A spare disk replaces failed Disk 1. For every stripe in the array, the controller performs exactly the reconstruction of Step 2, then writes the result to the spare. For an array with MM stripes, this is O(M)O(M) full-stripe reads across all surviving disks plus O(M)O(M) writes to the spare — i.e., a complete sequential scan of the array's logical capacity, which is why rebuild time tracks disk size, not just array size.

Common pitfalls & traps

PitfallWhy it happensConsequence
Treating RAID as a backupConfusing "disk fault tolerance" with "data protection"No recovery from deletion, ransomware, or logical corruption
Using RAID 5 on very large drivesRebuild time scales with capacity; long degraded windowSecond failure or unrecoverable read error during rebuild loses the whole array
Ignoring the write penalty in capacity planningAssuming parity RAID performs like RAID 0 on writesOLTP/database workloads bottleneck on 4–6x I/O amplification
Forgetting parity rotation in RAID 4Fixing parity to one disk creates a serialization pointThat single disk becomes the write bottleneck for the whole array
Assuming RAID 6 tolerates any 3 failuresMisreading "double parity" as "arbitrarily fault tolerant"A third simultaneous failure still causes data loss
Off-by-one in stripe/parity index mathConfusing stripe index with disk index when rotating parityReads or writes hit the wrong physical disk, corrupting data

Pioneers & history

RAID emerged from a simple economic and reliability insight at UC Berkeley: cheap, small disks connected redundantly could out-perform and out-cost expensive mainframe-class drives, provided the redundancy math was sound.

Origins · 1 minDavid A. Patterson and how this was discovered
DA
David A. Patterson
born 1947 · University of California, Berkeley

Co-author, with Garth Gibson and Randy Katz, of the 1988 paper "A Case for Redundant Arrays of Inexpensive Disks (RAID)," which formalized the RAID levels still used today. Patterson's broader work on computer architecture (including RISC processor design) shares the same philosophy visible in RAID: build reliable, high-performance systems out of many cheap, well-understood components rather than fewer expensive, exotic ones.

History
1987
— Berkeley researchers begin formalizing striping and parity schemes for arrays of inexpensive disks.
1988
— Patterson, Gibson, and Katz publish "A Case for Redundant Arrays of Inexpensive Disks (RAID)," defining RAID levels 1 through 5.
1990
s — RAID controllers become standard in enterprise servers; hardware RAID cards with battery-backed caches proliferate.
1997
— the RAID 6 dual-parity scheme is formalized and standardized to survive two simultaneous disk failures.
2000
s–present — software RAID and RAID-aware, checksummed file systems (ZFS, Btrfs) integrate redundancy directly with data-integrity verification.

Where this matters

RAID underlies nearly every layer of durable storage infrastructure. Database servers use RAID 10 for write-heavy transactional workloads where the 4x small-write penalty of RAID 5 is unacceptable. Large-scale file servers and NAS appliances default to RAID 5 or RAID 6 to balance capacity against fault tolerance for mostly-read workloads. Cloud object storage systems (the logical descendants of RAID's ideas) generalize parity across whole machines rather than disks — erasure coding schemes in systems like distributed object stores are direct mathematical generalizations of RAID 6's dual-parity idea to tolerate multiple node failures across a data center. Hardware RAID controllers with battery-backed write caches, software RAID implementations built into operating system kernels, and checksummed file systems that layer integrity verification atop RAID's fault tolerance are all direct engineering descendants of the ideas developed here. The same striping-versus-redundancy trade-off reasoning reappears whenever engineers design any distributed, fault-tolerant storage system, making RAID the essential mental model before studying replication and erasure coding at data-center scale.

Ask the tutor: How would erasure coding across an entire data center generalize the RAID 6 dual-parity idea to survive multiple machine failures?

Is RAID a substitute for backups?

No. RAID protects against physical disk hardware failure by keeping the array operational when a drive dies, but it does nothing against accidental file deletion, ransomware encrypting your files, a buggy application overwriting good data with garbage, or a file-system-level corruption bug. In all of those cases, the corrupted or deleted state gets faithfully mirrored or parity-protected right alongside the good state, because RAID only knows about disk-level blocks, not the meaning of the data. A real backup strategy needs a separate, versioned, and ideally offline or immutable copy of data, independent of the RAID layer entirely.

Why does RAID 5 have a "write penalty" but RAID 0 does not?

RAID 0 has no redundancy to maintain, so a write to one stripe unit is just one direct disk write. RAID 5 must keep a parity block consistent with all the data blocks in its stripe, and updating a single data block changes what that stripe's parity should be. Because reading the entire stripe to recompute parity from scratch is often more expensive than an incremental approach, RAID 5 instead reads the old data and old parity, XORs them together with the new data to get new parity, then writes both the new data and new parity — four total I/Os for what would be one I/O on a plain disk.

When should I choose RAID 10 over RAID 5 or RAID 6?

Choose RAID 10 (striped mirrors) when write performance and rebuild speed matter more than raw capacity efficiency. RAID 10 rebuilds a failed disk by simply copying its mirror partner — a single-disk-speed operation — rather than reconstructing from parity across the whole array, so rebuild is much faster and the array stays fully redundant elsewhere during that time. The cost is capacity: RAID 10 only gives 50% usable capacity versus RAID 5's much higher efficiency, so it is favored for databases and other write-heavy, latency-sensitive workloads where that capacity cost is acceptable.

Can RAID 6 survive any three simultaneous disk failures?

No — RAID 6 is specifically engineered to tolerate exactly two simultaneous failures per stripe, using two independent parity equations (an XOR parity and a Galois-field-based parity). With two equations you can solve for at most two unknowns; a third simultaneous failure introduces a third unknown into a system that only has two independent equations, making it mathematically unsolvable, so data in that stripe is permanently lost. Achieving three-failure tolerance would require a third independent parity function, at additional capacity and computation cost, which some erasure-coding schemes beyond standard RAID do provide.

What is a "full-stripe write" and why does it matter for performance?

A full-stripe write occurs when all the data blocks in one stripe are overwritten at once, so the controller can compute new parity directly as the XOR of the new data blocks without ever reading the old data or old parity from disk. This avoids the expensive read-old/write-new cycle behind the small-write penalty, dropping the I/O cost per stripe from roughly 4N4N (for NN separate small writes) to about N+1N+1. Log-structured and copy-on-write file systems are designed to batch writes into full stripes specifically to exploit this, making RAID 5/6 far more competitive on write-heavy sequential or batched workloads.

Does RAID protect against silent data corruption ("bit rot")?

Only partially. Standard RAID parity can detect that something is inconsistent within a stripe, but with only single parity (RAID 4/5) it generally cannot determine which of the N+1N+1 disks holds the corrupted block, since the parity equation alone does not identify the faulty operand. Modern checksummed file systems (such as ZFS or Btrfs) solve this by storing an independent checksum per block on top of RAID's redundancy, so the system can identify exactly which copy is corrupt and use the RAID redundancy to repair it — combining RAID's fault tolerance with an explicit data-integrity layer.

Why is RAID 4's dedicated parity disk considered obsolete compared to RAID 5?

In RAID 4, every single write to any data disk also requires updating the one dedicated parity disk, so that parity disk receives write traffic proportional to the entire array's write load, no matter how many data disks there are. This turns the parity disk into a hard performance ceiling as the array grows, since it cannot be parallelized away. RAID 5 removes this bottleneck by rotating which physical disk holds parity from stripe to stripe, spreading the parity-write load evenly across all disks, which is why RAID 5 almost entirely displaced RAID 4 in practice despite both offering identical capacity and single-fault tolerance.

Before this