Solid-State Drives (SSDs)

A database server writes a four-kilobyte log record. It expects the write to finish in microseconds. On a rotational disk, that write may wait for a platter to spin. It also waits for an arm to seek. This can cost several milliseconds. On a solid-state drive, there is no arm and no platter. Yet the write is still surprisingly hard to do quickly. This is because of a quirk of the underlying memory: you cannot overwrite a single page of NAND flash in place. You must erase an entire block first. Erasing is slow, coarse, and destructive to the memory cells themselves. Understanding SSDs means understanding how an entire firmware layer, the Flash Translation Layer (FTL), hides this quirk from the operating system. It makes flash memory look like an ordinary block device. SSDs matter because they have quietly replaced rotational disks. They are now the default storage medium in laptops, phones, and cloud data centers. Every major file system, database engine, and virtual memory subsystem now assumes storage with no seek penalty. But this storage does have write amplification, limited erase endurance, and background maintenance traffic. That traffic competes with foreground I/O. A student who understands Disk Structure and Geometry for spinning disks must relearn nearly every performance intuition for flash. Sequential versus random access, wear, and latency all behave differently. The big picture is this: NAND flash cells can be read cheaply. They can be programmed (written) only from a fully erased state. They can be erased only in large chunks called blocks, not individually. The FTL maps logical block addresses (LBAs) that the OS uses onto physical flash pages. It always writes new data to already-erased locations rather than overwriting in place. It reclaims space later through a background process called garbage collection. Wear leveling spreads erase cycles evenly across the chip. This is needed because each cell tolerates only a finite number of erases before it wears out. This article builds the mental model from the bottom up. It covers the physics and geometry of NAND flash. It explains why erase-before-write forces an indirection layer. It shows how the FTL's mapping table works. It covers how garbage collection reclaims stale pages. It explains how wear leveling extends drive lifetime. It also compares all of this — quantitatively — to rotational disks. It closes with the performance and reliability tradeoffs that motivate SSD-aware file system design. This is the natural next topic in Allocation Methods for File Storage and File-System Implementation and Layout.

Key takeaways
  • NAND flash is organized into pages (read/write unit, ~4–16 KB) grouped into blocks (erase unit, ~128–256 pages).
  • You can only write to a page that has been erased; you cannot overwrite a page in place — this is erase-before-write.
  • The Flash Translation Layer (FTL) maps logical addresses to physical flash locations and always writes out-of-place.
  • Garbage collection reclaims blocks full of stale (overwritten) pages by copying live pages elsewhere, then erasing.
  • Wear leveling spreads erases evenly across blocks because each cell tolerates only a bounded number of program/erase cycles.
  • SSDs eliminate seek/rotation latency entirely but introduce write amplification — a cost with no analogue on rotational disks.
The core insight

An SSD is fast and durable only because its firmware, the Flash Translation Layer, converts a physical memory that can only be erased in bulk into a logical block device that appears freely overwritable.

The idea

A solid-state drive stores data in NAND flash memory, arrays of floating-gate or charge-trap transistors that retain charge without power. The smallest unit the controller can read or program (write) is a page, typically 4 KB, 8 KB, or 16 KB. Pages are grouped into blocks, typically 128 to 512 pages, and the block — not the page — is the smallest unit that can be erased. This asymmetry is the defining physical fact of flash: programming pushes electrons onto a floating gate one page at a time, but removing them (erasing) requires a high-voltage pulse applied to an entire block simultaneously, because the erase circuitry is wired at block granularity. The consequence is erase-before-write: a page holding stale data cannot simply be overwritten with new data. The controller must first erase the entire block containing that page — destroying every other page in it, live or not — before any page in it can be reprogrammed. Since erasing is roughly ten to a hundred times slower than programming, and a block may contain hundreds of live pages you do not want to lose, no drive erases on every write. Instead, the Flash Translation Layer (FTL) — firmware running on the SSD's embedded controller — intercepts every logical read/write request from the OS and translates it into physical NAND operations, always directing new writes to pre-erased pages elsewhere on the chip. This is called out-of-place update (or log-structured writing), and it is the single idea that makes flash usable as a general-purpose block device. The FTL maintains a mapping table (logical block address → physical page), tracks which pages are stale after each out-of-place update, and periodically runs garbage collection to reclaim blocks whose live pages have been relocated and whose stale pages can finally be erased. Because each flash cell degrades with every program/erase (P/E) cycle and eventually fails, the FTL also performs wear leveling, deliberately migrating data so that no block is erased far more often than any other.

How it works

The FTL sits between the host interface (SATA/NVMe queue of logical reads and writes) and the raw NAND chips. Four mechanisms cooperate:

1. Address mapping. The FTL keeps a table translating each logical page number (LPN) to a physical page number (PPN). Fine-grained (page-level) mapping gives maximum flexibility at the cost of table size (~4 bytes per 4 KB page, roughly 1 GB of RAM per 1 TB of flash for pure page mapping); most real drives use hybrid mapping — block-level mapping with a small per-block log of page offsets — to shrink this table.

2. Out-of-place write. A write to LPN L never overwrites the page currently holding L's data. The FTL picks a pre-erased page in the current "open" block, writes the new data there, updates the mapping table so L now points to the new PPN, and marks the old PPN invalid (stale).

3. Garbage collection (GC). Over time, blocks accumulate a mix of valid and invalid pages. When free (erased) space runs low, the FTL selects a victim block, copies its still-valid pages to a fresh location (updating the mapping table again), and then erases the victim block, returning it to the free pool.

4. Wear leveling. The FTL tracks an erase count per block and biases victim/target selection so that cold (rarely-updated) data occasionally migrates out of lightly-worn blocks and infrequently-erased blocks are preferentially reused, keeping P/E cycle counts roughly uniform across the chip. ``` // Simplified Flash Translation Layer State: map[LPN] -> PPN // logical-to-physical page table valid[PPN] -> boolean // is this physical page still live? eraseCount[block] -> integer // wear-leveling statistic freeList // erased blocks ready to receive writes openBlock // block currently accepting new page writes function WRITE(LPN, data): if map[LPN] exists: oldPPN = map[LPN] valid[oldPPN] = false // old copy becomes stale, NOT overwritten if openBlock has no free page: openBlock = ALLOCATE_BLOCK() // pull an erased block from freeList PPN = next free page in openBlock PROGRAM(PPN, data) // physical NAND program operation map[LPN] = PPN valid[PPN] = true if freeList.size() < LOW_WATERMARK: GARBAGE_COLLECT() function READ(LPN): PPN = map[LPN] return PHYSICAL_READ(PPN) function ALLOCATE_BLOCK(): if freeList.isEmpty(): GARBAGE_COLLECT() return freeList.popLowestEraseCount() // wear leveling: prefer lightly-erased blocks function GARBAGE_COLLECT(): victim = SELECT_VICTIM_BLOCK() // e.g. block minimizing valid-page count for each PPN in victim.pages: if valid[PPN]: newPPN = write live page's data via WRITE-style relocation map[ownerLPN(PPN)] = newPPN valid[newPPN] = true ERASE(victim) // block-granularity erase eraseCount[victim] += 1 freeList.push(victim) function SELECT_VICTIM_BLOCK(): // greedy: choose block with fewest valid pages (least copying cost) return argmin over blocks not in freeList of (count of valid pages in block) ``` The read path is straightforward: look up the mapping, read one physical page. The write path is where all the complexity lives, because every write can potentially trigger, as a side effect, a garbage-collection pass that itself performs additional reads, writes, and an erase — this cascading cost is exactly what "write amplification" measures.

Figure
NAND flash geometry and out-of-place writes inside a block
Block B0 — 8 pages (erase unit = whole block) Before write to LPN=5: LPN 3 LPN 5 stale LPN 9 free free free free After write to LPN=5 (new data goes to a fresh page, old page marked invalid): LPN 3 stale stale LPN 9 LPN 5' free free free Garbage collection: copy live pages (3, 9) elsewhere, then ERASE whole block: entire block erased — all pages free again valid page stale (invalid) newly written erased/free

Complexity & correctness

Time complexity of the core operations. A read is O(1): one mapping-table lookup plus one physical page read (a NAND page read costs roughly 25–100 microseconds regardless of which page is read, unlike a rotational disk where physical location matters enormously). A write is amortized O(1) mapping-table work plus one page program (~200–500 microseconds), but garbage collection is triggered periodically and its cost must be amortized across writes: each GC pass copies v valid pages (a page read plus a page program each) and performs one block erase (~2–5 milliseconds), reclaiming (pages per block − v) free pages. If a drive maintains free-space invariant by running GC whenever free blocks drop below a watermark, the amortized extra cost per host write is proportional to the write amplification factor (WAF):

WAF=total pages physically programmedpages requested by the host\text{WAF} = \frac{\text{total pages physically programmed}}{\text{pages requested by the host}}

WAF is never less than 1 (every host write requires at least one physical program) and grows as the fraction of valid data relocated during GC grows. A drive kept nearly full, with data updated randomly in small chunks, forces GC to relocate many still-live pages per reclaimed block, driving WAF up toward 3–6× in adversarial workloads; a mostly-empty drive receiving sequential writes can approach WAF ≈ 1.

Space complexity. The mapping table for page-level FTL requires O(N) entries where N is the number of logical pages — for a 1 TB drive with 4 KB pages that is 2.56 × 10⁸ entries, each needing roughly 4 bytes (32-bit PPN), i.e. about 1 GB of controller RAM. Hybrid or block-level mapping reduces this to O(N / pages-per-block) primary entries plus small per-block logs, trading RAM for slightly slower address translation on sequential-update blocks. Over-provisioning — reserving a fraction (commonly 7–28%) of total flash capacity that the host never sees as usable LBAs — is what gives the GC algorithm slack: it guarantees a supply of erased blocks exists even under worst-case fragmentation, directly lowering the achievable WAF.

Correctness argument. The FTL must preserve two invariants under arbitrary interleavings of reads, writes, and power loss: 1. Mapping invariant — at all times, `map[L]` points to the most recently written physical copy of logical page L, or is undefined if L was never written. 2. No data loss on erase — a block is erased only after every page in it holding still-referenced (valid) data has been copied elsewhere and `map[]` updated to point at the copy, and that update is durably committed (e.g., logged in an out-of-band area or a separate mapping journal) before the erase proceeds. Out-of-place writing preserves invariant 1 trivially: a write never destroys the old copy until the mapping is repointed, so a crash mid-write leaves the old valid copy still reachable (the new copy is simply not yet referenced, so it is treated as garbage on recovery, not committed data — this is the same idea as Interrupts and DMA completion ordering, where a transfer is not "done" until its completion is acknowledged). Garbage collection preserves invariant 2 by construction: the algorithm explicitly copies every page marked `valid` before erasing, and marks a page invalid only after its logical owner has a newer copy recorded in the map. The correctness of wear leveling is a liveness property, not a safety property: it does not affect whether data is preserved, only whether the drive's useful lifetime is maximized, since a block that reaches its rated P/E-cycle limit is retired and its capacity lost.

Tradeoffs, honestly stated. SSDs remove the mechanical seek/rotation delay entirely, so random 4 KB reads that take 5–10 ms on a rotational disk take tens of microseconds on flash — three orders of magnitude faster, a real and important win. But they introduce three costs with no rotational-disk analogue: write amplification (extra physical writes beyond what the host requested), finite endurance (each cell tolerates a bounded number of P/E cycles, typically 1,000–100,000 depending on flash type), and background GC latency spikes (a foreground write can stall behind an in-progress erase). None of these costs exist on a spinning platter, which can be overwritten in place indefinitely.

Worked example

Consider a toy SSD with blocks of 4 pages (real drives use hundreds; small numbers make the trace legible) and exactly 2 blocks total, B0 and B1, both initially erased. The host issues these logical writes in order: `W(L1)`, `W(L2)`, `W(L3)`, `W(L4)`, `W(L1)` again (an update), `W(L5)`.

StepOperationAction takenB0 pages (p0..p3)B1 pages (p0..p3)map
0—both blocks erasedfree,free,free,freefree,free,free,free{}
1W(L1)program B0.p0L1,free,free,freefree×4L1→B0.p0
2W(L2)program B0.p1L1,L2,free,freefree×4L1→B0.p0, L2→B0.p1
3W(L3)program B0.p2L1,L2,L3,freefree×4+L3→B0.p2
4W(L4)program B0.p3 — B0 now fullL1,L2,L3,L4free×4+L4→B0.p3
5W(L1) againold B0.p0 → stale; new copy to B1.p0 (open new block)stale,L2,L3,L4L1,free,free,freeL1→B1.p0
6W(L5)program B1.p1stale,L2,L3,L4L1,L5,free,free+L5→B1.p1

At this point B0 has 3 valid pages (L2, L3, L4) and 1 stale page — no free pages remain in B0, and no block is fully erased except none, since B1 still has 2 free pages. Suppose the host now issues `W(L6)`, `W(L7)` filling B1 completely. All pages are now full and no free blocks remain, so the next write must trigger `ALLOCATE_BLOCK` → `GARBAGE_COLLECT`.

GC step. `SELECT_VICTIM_BLOCK` picks the block minimizing valid-page count. B0 has 3 valid pages (L2, L3, L4) and 1 stale; B1 has 4 valid pages and 0 stale. B0 is the cheaper victim (fewer pages to copy). The algorithm: 1. Copies L2, L3, L4 to a fresh already-erased target block B2 (obtained from the free list, or — since none exists yet — GC must have reserved at least one over-provisioned erased block for exactly this purpose). 2. Updates `map[L2] = B2.p0`, `map[L3] = B2.p1`, `map[L4] = B2.p2`. 3. Erases B0 entirely (all 4 pages, including the already-stale L1 copy) — this is the physically destructive, block-granular operation. 4. Increments `eraseCount[B0]` and returns B0 to the free list.

Write amplification for this GC pass: the host originally requested 7 writes (L1..L7, counting the L1 update once as one logical write event, twice as two physical programs since it's rewritten). The GC pass added 3 extra physical page programs (copying L2, L3, L4) that the host never asked for. If we count the total physical programs (8: L1 old + L2 + L3 + L4 + L1 new + L5 + L6 + L7, then +3 GC copies = 11) against host-requested logical writes (7), WAF for this trace is 11⁄7 ≈ 1.57 — illustrating concretely how "hot" overwritten data (L1) combined with a nearly-full drive forces GC to pay a real copying cost.

Common pitfalls & traps

PitfallWhy it happensConsequence
Assuming in-place overwrite, like a rotational disk sectorIgnoring erase-before-write physicsLeads to modeling SSD writes as O(1) with no amplification, badly underestimating tail latency
Treating random and sequential SSD writes as equally cheapTrue there's no seek penalty, but GC cost still differsRandom small writes fragment blocks and raise WAF; sequential writes leave whole blocks stale together, cheap to reclaim
Ignoring over-provisioning when reasoning about "drive full" behaviorConfusing advertised capacity with physical capacityUnderestimates how much slack GC actually has; near-100%-full drives (by host accounting) still have spare erased blocks
Forgetting wear leveling must move cold (rarely written) data tooNaively erasing only "dirty" blocks under-uses lightly-worn onesCold data can sit in low-erase-count blocks forever, causing uneven wear and premature failure of hot blocks
Believing TRIM/discard is optionalNot realizing the FTL cannot know a deleted file's pages are garbage without being toldWithout TRIM, deleted-but-unnotified pages are copied by GC as if still valid, inflating WAF unnecessarily

Pioneers & history

Origins · 1 minFujio Masuoka and how this was discovered
FM
Fujio Masuoka
b. 1943 · Toshiba research engineer

Fujio Masuoka invented both NOR and NAND flash memory while at Toshiba in the 1980s, seeking a memory that could be erased in large blocks — faster and cheaper than the byte-erasable EEPROM it replaced. The name "flash" was reportedly suggested by a colleague because the bulk-erase operation reminded him of a camera flash. Masuoka's NAND architecture, optimized for high density over random-access speed, became the foundation of every modern SSD, USB drive, and smartphone storage chip.

History
1980
— Fujio Masuoka conceives the floating-gate flash memory cell at Toshiba.
1987
— Toshiba announces NAND flash architecture, prioritizing density and block-erase speed over NOR's byte-addressability.
1991
— SanDisk (then SunDisk) ships early flash-based solid-state storage for industrial and military use.
2007
— mainstream consumer SSDs appear in laptops as NAND prices fall and the FTL matures into commodity firmware.
2011
onward — NVMe protocol and 3D NAND stacking arrive, letting SSDs exploit massive internal parallelism and higher density that SATA and 2D NAND could not support.

Where this matters

Every modern laptop, phone, and cloud server relies on an FTL running inside its storage device, invisible to the operating system's block layer. Data-center flash arrays run FTLs tuned for enormous over-provisioning and aggressive wear leveling because they sustain write-heavy database and log workloads around the clock. NVMe SSDs expose many parallel command queues so the OS can issue thousands of concurrent I/Os, exploiting the fact that flash chips, unlike a single spinning platter, can service many reads and writes to different chips simultaneously — a property that reshapes how the OS scheduler and interrupt/DMA completion paths (see Interrupts and DMA) are designed for storage. File systems increasingly issue TRIM/discard commands to tell the FTL which logical pages are no longer needed, directly reducing garbage-collection work and write amplification — a cooperation between OS and device that has no equivalent on rotational media and motivates SSD-aware choices in Allocation Methods for File Storage and log-structured or copy-on-write designs discussed under File-System Implementation and Layout. Databases favor log-structured merge trees partly because their append-mostly write pattern naturally matches how the FTL itself already prefers to receive data.

Ask the tutor: How does the choice of victim-block selection policy in garbage collection (e.g., greedy least-valid-pages vs. cost-benefit heuristics) actually change the achievable write amplification under realistic workloads?

Why can't the FTL just erase a page every time it's rewritten, avoiding the mapping-table complexity?

Because erase is a block-granularity operation, not a page-granularity one — the high-voltage circuitry that removes charge from floating gates is wired across an entire block simultaneously, typically hundreds of pages. Erasing on every page write would mean destroying and rewriting hundreds of other, still-valid pages every time a single page changes, which is both extremely slow (erase takes milliseconds, program takes microseconds) and would multiply wear on unrelated data enormously. Writing out-of-place and deferring the erase to a background garbage-collection pass, amortized across many writes, is dramatically cheaper, which is precisely why the FTL and its mapping indirection exist at all.

How does over-provisioning actually reduce write amplification?

Over-provisioning reserves flash capacity beyond what the host sees as addressable LBAs, so the drive always has spare erased blocks available even when the host believes the drive is "full." With more spare capacity, garbage collection can choose victim blocks with fewer valid pages to relocate (since there's more slack before free space runs critically low), directly lowering the number of live-page copies per erase reclaimed. Empirically and in analytic models, WAF drops sharply as over-provisioning rises from near 0% toward 25–28%, which is why enterprise SSDs often report smaller usable capacity than their raw flash capacity.

Does TRIM/discard actually change how the FTL behaves, or is it just advisory?

TRIM is a command the file system issues to tell the SSD "logical block X no longer holds live data" after a file is deleted — information the FTL cannot infer on its own, since from the drive's perspective the old data still looks valid until overwritten. Without TRIM, garbage collection must treat those pages as valid, copying them during every GC pass even though no one will ever read them again, needlessly inflating write amplification. With TRIM, the FTL can mark the corresponding physical pages invalid immediately, letting GC skip them — this is a real behavioral change, not merely advisory bookkeeping, and its absence is a common cause of SSD slowdown on file systems or operating systems that don't issue it.

Why do SSDs eventually wear out, and what does "P/E cycle" mean?

Each program/erase (P/E) cycle physically stresses the oxide layer insulating a flash cell's floating gate, gradually degrading its ability to reliably trap and retain charge. After a rated number of cycles — historically around 100,000 for older single-level-cell (SLC) flash, but far fewer (1,000–3,000) for denser multi-level and triple-level cell (MLC/TLC) flash — a block accumulates enough physical damage that it can no longer reliably store data and is retired from use, reducing the drive's usable capacity. Wear leveling delays this outcome by ensuring erases are spread evenly, so no single block is singled out for disproportionate stress while others remain nearly untouched.

Is a page-level mapping table always better than a block-level one?

Not universally — it's a space/flexibility tradeoff. Page-level mapping gives the FTL maximum freedom to write any logical page to any physical page, minimizing unnecessary data movement, but requires one mapping entry per page, consuming roughly 1 GB of controller RAM per terabyte of flash. Block-level (or hybrid) mapping shrinks the table by an order of magnitude by mapping whole blocks and keeping only small per-block logs for recent updates, but constrains how freely pages can be relocated, sometimes increasing the copying cost during garbage collection. Most commercial drives use a hybrid scheme calibrated to their DRAM budget and workload.

How is an SSD's random-read performance able to beat a rotational disk by orders of magnitude?

A rotational disk's random read cost is dominated by mechanical delay: the actuator arm must physically seek to the correct track (several milliseconds) and then wait for the platter to rotate the target sector under the head (average half a rotation, also milliseconds). NAND flash has no moving parts — reading any page costs a fixed electrical sensing operation of tens of microseconds, regardless of which page, because there is no physical arm to move or track to wait for. This is precisely why SSDs are transformative for random-access-heavy workloads like databases and virtual memory paging, even though sequential throughput gains, while real, are comparatively smaller.

Why does an SSD sometimes get slower the longer you use it, even without any hardware failure?

As a drive fills with data and undergoes repeated overwrites, blocks accumulate a mixture of valid and stale pages, and free (erased) space shrinks toward the over-provisioned reserve. Garbage collection then must run more frequently and often relocate more valid pages per reclaimed block, since fewer blocks are "mostly stale" and cheap to reclaim. This raises write amplification and can introduce visible latency spikes when a foreground write stalls behind an in-progress background erase — a phenomenon sometimes called "SSD slowdown" or "the cliff," which manufacturers mitigate with larger over-provisioning and smarter victim-selection heuristics.

Before this

Where this leads