Chapter 10

SSD와 플래시 컨트롤러

8장과 9장에서 본 NAND 플래시는 덮어쓸 수 없고, 쓰는 단위와 지우는 단위가 다르며, 몇천 번 지우면 닳는 까다로운 소자다. 그런데 운영체제는 SSD를 "아무 주소에나 몇 번이고 덮어쓸 수 있는 디스크"로 쓴다. 이 간극을 메우는 것이 SSD 컨트롤러와 그 안의 펌웨어 — 플래시 변환 계층(FTL)이다. 이 장에서는 주소 매핑, 가비지 컬렉션, 쓰기 증폭, 웨어 레벨링, SLC 캐시, 그리고 NVMe 인터페이스까지 SSD가 '빠르고 오래가는 디스크'인 척하는 기술을 하나씩 해부한다.

SSD 해부: 컨트롤러 · NAND · DRAM

M.2 2280 SSD 기판을 뒤집어 보면 부품은 의외로 단출하다. 가운데에 컨트롤러(Controller SoC) 하나, 그 옆에 DRAM 칩 하나(없는 제품도 있다), 그리고 나머지 면적을 차지하는 NAND 패키지 2~4개. 여기에 전원 관리 IC(PMIC)와, 기업용 제품이라면 정전 시 버퍼를 NAND에 쏟아낼 시간을 벌어 주는 커패시터 뱅크(PLP, Power Loss Protection)가 더해진다.

호스트 CPU · 메모리 PCIe ×4 SSD 컨트롤러 (SoC) 호스트 인터페이스 PCIe PHY · NVMe CPU 코어 Arm Cortex-R ×3~5 FTL 펌웨어 매핑 · GC · WL ECC 엔진 LDPC (11장) SRAM 버퍼 · 암호화 AES-256 암호화 플래시 인터페이스 채널 0 ~ 7 (ONFI/Toggle) DRAM 컨트롤러 LPDDR4X / DDR4 전력 · 온도 관리 스로틀링 · PS 상태 DRAM 캐시 매핑 테이블 ≈ 1 GB / 1 TB PMIC · PLP 커패시터 (기업용) CH0 CH1 CH2·3 CH4·5 CH6·7 NAND 0 다이 4~16 적층 NAND 1 1 Tb/다이 (TLC) NAND 2 200~300단 (9장) NAND 3 채널당 8비트 버스 NAND 패키지 (BGA)
그림 10-1. 전형적인 PCIe 4.0 ×4 NVMe SSD의 블록도. 컨트롤러는 여러 개의 실시간용 Arm 코어와 LDPC ECC 엔진, 8개 안팎의 NAND 채널을 가진 SoC다. DRAM은 주로 사용자 데이터가 아니라 주소 매핑 테이블을 담는다. 각 NAND 패키지에는 다이가 여러 장 적층되어 있다.

컨트롤러가 하는 일은 크게 네 갈래다. ① 호스트 인터페이스: PCIe 링크를 통해 NVMe 명령을 받고 데이터를 DMA로 주고받는다. ② FTL: 호스트가 주는 논리 주소(LBA)를 NAND의 물리 위치로 번역하고, 가비지 컬렉션과 웨어 레벨링을 돌린다. ③ 데이터 무결성: 11장에서 다룰 LDPC 부호로 비트 오류를 정정하고, 읽기 재시도(read retry)와 리프레시로 데이터 보존을 관리한다. ④ NAND 구동: 여러 채널로 수십 개의 다이에 명령을 뿌리고 그 타이밍을 스케줄링한다.

구성 요소역할대표 수치 (2024~2026 소비자·데이터센터 제품)
컨트롤러FTL, ECC, 호스트/NAND 인터페이스Arm Cortex-R 계열 3~8코어, 8~16채널, 5~12 nm 공정
NAND 다이데이터 저장TLC 1 Tb(128 GB)/다이, 16 KB 페이지, 4~6 플레인, I/O 2.4~3.6 GT/s
DRAM매핑 테이블 캐시용량의 약 1/1000 (1 TB → 1 GB), LPDDR4X/DDR4
HMBDRAM-less 제품의 대안호스트 DRAM 수십 MB 차용 (NVMe 1.2 기능)
호스트 링크PCIe + NVMeGen4 ×4 ≈ 7 GB/s, Gen5 ×4 ≈ 14 GB/s
PLP정전 시 휘발 버퍼 보호탄탈/폴리머 커패시터 수십 ms 유지 (주로 기업용)
DRAM-less SSD와 HMB

저가형 SSD는 원가와 전력을 줄이려 DRAM을 뺀다. 대신 NVMe의 HMB(Host Memory Buffer) 기능으로 호스트 PC의 메인 메모리 일부(보통 수십 MB)를 PCIe 너머로 빌려 매핑 테이블의 '자주 쓰는 부분'만 캐싱한다. 1 TB 전체 테이블(~1 GB)은 못 담지만, 실제 워크로드는 지역성(1장)이 있어 좁은 LBA 범위에 몰리므로 일상 사용에서는 차이가 작다. 넓은 범위에 랜덤 접근이 퍼지면 매핑 미스가 나서 NAND에서 매핑 정보를 먼저 읽어야 하므로 지연이 늘어난다.

병렬성: 채널 · 웨이 · 다이 · 플레인

NAND 다이 하나는 결코 빠르지 않다. TLC 다이가 한 번 프로그램하는 데 수백 µs가 걸리고, 읽기(\(t_R\))도 수십 µs다. 다이 하나의 순차 쓰기 속도는 기껏해야 수백 MB/s. 7 GB/s짜리 SSD는 이런 느린 다이 수십 개를 동시에 돌려서 만든다. 병렬성은 계층적으로 조직된다.

컨트롤러 CH0 채널 버스 (8비트, 공유) 다이 0 (Way 0, CE0) P0P1P2P3 다이 1 (Way 1, CE1) 다이 2 (Way 2, CE2) 다이 3 (Way 3, CE3) 플레인 0 블록 0 블록 1 블록 2 ⋮ 블록 N−1 블록 수백~수천 개 + 페이지 버퍼 (플레인마다 독립) 블록 = 소거 단위 페이지 0 · 16 KB 페이지 1 페이지 2 ⋮ 페이지 M−1 페이지 = 읽기·쓰기 단위 M ≈ 수백~2천+ 페이지 블록 ≈ 수 MB~수십 MB + 스페어 영역 (ECC 패리티)
그림 10-2. 병렬성의 계층. 한 채널의 버스에 여러 다이(웨이, 칩 선택 신호 CE#로 구분)가 매달려 버스를 시분할한다. 다이 안의 플레인은 각자 페이지 버퍼를 가져 동시에 읽고 쓸 수 있다(multi-plane 동작). 플레인은 블록으로, 블록은 페이지로 나뉜다.

핵심은 느린 셀 동작(\(t_{PROG}, t_R\))과 빠른 데이터 전송을 겹치는 것이다. 다이 0이 데이터를 받아 수백 µs 동안 프로그램하는 동안, 채널 버스는 비어 있으니 다이 1, 2, 3에 데이터를 차례로 보낸다. 이것이 웨이 인터리빙(way interleaving)이다. 여러 채널은 서로 완전히 독립이므로 채널 수만큼 곱절로 늘어난다. 다이 하나의 쓰기 처리량과 채널 하나의 처리량은 다음과 같다.

$$ BW_{die} = \frac{N_{plane}\, S_{prog}}{t_{xfer} + t_{PROG}}, \qquad BW_{ch} = \min\!\left( N_{way}\, BW_{die},\ \ BW_{bus} \right), \qquad BW_{SSD} = \min\!\left(N_{ch} BW_{ch},\ BW_{host}\right) $$
여기서 \(S_{prog}\)는 플레인당 한 번에 프로그램하는 데이터(TLC one-shot 프로그램이면 16 KB × 3 = 48 KB), \(t_{xfer} = N_{plane} S_{prog}/BW_{bus}\)는 그 데이터를 채널로 보내는 시간, \(BW_{bus}\)는 채널 버스 속도(8비트 × 2.4 GT/s = 2.4 GB/s)다.

예를 들어 4플레인 TLC 다이(\(t_{PROG}\) = 500 µs)는 한 사이클에 192 KB를 쓰고 전송에 80 µs가 걸리므로 다이당 약 330 MB/s다. 1 Tb 다이 8개로 만든 1 TB SSD는 SLC 캐시(7절)를 쓰지 않으면 약 2.6 GB/s가 한계다. 같은 컨트롤러라도 4 TB 제품(다이 32개)이 더 빠른 이유가 여기 있다. 읽기는 \(t_R\)이 짧아 오히려 채널 버스가 병목이 되기 쉽다.

SIMULATOR

채널 × 웨이 × 플레인 병렬성과 타임라인

동작
셀 방식
다이당 플레인
채널 I/O 속도
호스트 링크 (×4)
총 다이 수 (용량 @1 Tb)—
다이당 처리량—
채널 버스 점유율—
NAND 합계—
최종 처리량 / 병목—
간트 차트는 채널 0 하나를 보여 준다. 보라 = 채널 버스로 데이터 전송, 청록 = 다이 내부 동작(tPROG 또는 tR). 해볼 것: ① 웨이 1에서 쓰기 — 버스가 대부분 놀고 있다. 웨이를 2, 4로 올리며 처리량이 비례해 느는 것을 보자. ② 읽기로 바꾸면 웨이 2만 되어도 버스가 포화된다(병목 = 채널). ③ QLC로 바꾸면 tPROG가 길어져 같은 다이 수로 쓰기가 크게 느려진다. ④ 채널 16 × 웨이 4에서 Gen4 링크가 병목이 되는지 확인하자.
슈퍼블록과 스트라이핑

컨트롤러는 병렬성을 쉽게 쓰려고 모든 다이·플레인에서 같은 번호의 블록을 하나씩 묶어 슈퍼블록(superblock)이라는 논리 단위로 관리한다. 호스트 데이터는 슈퍼블록 안의 페이지들에 라운드로빈으로 흩뿌려져(striping) 모든 다이가 동시에 일한다. 대신 GC와 소거도 슈퍼블록 단위로 일어나므로, FTL이 보는 '블록'은 수백 MB~GB에 이를 수 있다. 슈퍼블록 안에 RAID-5 같은 다이 간 패리티(흔히 RAIN, XOR 패리티라 부른다)를 두어 다이 하나가 통째로 고장 나도 데이터를 복구한다.

플래시의 세 가지 제약

8장에서 NAND 셀은 플로팅 게이트(또는 차지 트랩)에 전자를 넣어 문턱 전압 \(V_t\)를 올리는 방식으로 프로그램한다고 했다. 이 물리가 SSD 설계의 모든 제약을 만든다.

  1. 덮어쓰기 불가(erase-before-write): 프로그램은 전자를 넣는 방향(\(V_t\) 상승, 비트 1→0)으로만 할 수 있다. 전자를 빼려면 기판 쪽에 고전압(~20 V)을 걸어 FN 터널링으로 한꺼번에 빼내는 소거가 필요하다. 이미 쓴 페이지는 소거 전까지 다시 쓸 수 없다.
  2. 단위 불일치: 쓰기·읽기 단위는 페이지(16 KB)인데 소거 단위는 수백~수천 페이지를 묶은 블록(수 MB~수십 MB)이다. 소거용 고전압이 웰(well) 전체에 걸리기 때문이다. 게다가 블록 안의 페이지는 워드라인 순서대로만 써야 한다(셀 간 간섭 보상 때문).
  3. 제한된 수명: 프로그램·소거(P/E) 사이클마다 터널 산화막에 결함이 쌓여 전자를 잃기 쉬워지고 \(V_t\) 분포가 넓어진다. TLC는 대략 1,000~3,000회, QLC는 수백~1,500회 정도에서 오류율이 ECC 한계에 다가간다(11장).
블록 (예: 8페이지로 단순화) P0 데이터 A P1 데이터 B P2 데이터 C P3 (빈 페이지) P4 (빈 페이지) P5 P6 P7 P3에 쓰기 ✓빈 페이지 → 가능 (순서대로) P1 덮어쓰기 ✗소거 전에는 불가 소거 = 블록 통째로 동작 시간 (TLC, 로그 눈금) 10 µs100 µs1 ms10 ms 읽기 t_R~50 µs 쓰기 t_PROG~500 µs 소거 t_BERS~3–5 ms P/E 수명 (대략, 2024~) SLC ~ 50k – 100k MLC ~ 3k – 10k TLC ~ 1k – 3k QLC ~ 0.5k – 1.5k
그림 10-3. NAND의 제약. 빈 페이지에는 순서대로 쓸 수 있지만 이미 쓴 페이지는 덮어쓸 수 없고, 지우려면 블록 전체를 소거해야 한다. 소거는 읽기보다 약 100배 느리다. 수명 수치는 제조사·세대·ECC 강도에 따라 크게 다르다.

만약 FTL 없이 LBA를 물리 위치에 고정한다면(블록 매핑의 극단), 4 KB 하나를 고칠 때마다 그 블록 전체(예: 24 MB)를 읽어 DRAM에 담고, 블록을 지우고, 수정된 전체를 다시 써야 한다. 4 KB 쓰기에 24 MB를 쓰는 셈이니 쓰기 증폭이 6,000배, 속도는 소거 시간 수 ms에 묶이고 자주 쓰는 블록은 금세 닳는다. FTL은 이 세 문제를 한꺼번에 푸는 소프트웨어 층이다.

FTL: 논리 주소를 물리 위치로

FTL(Flash Translation Layer)의 기본 아이디어는 간단하다. "고칠 데이터는 제자리에 쓰지 말고 빈 페이지에 새로 쓴 뒤, 주소록만 고친다." 이를 out-of-place 업데이트라 한다. 호스트가 보는 논리 블록 주소(LBA, 또는 FTL 단위로 묶은 LPN)마다 현재 데이터가 있는 물리 페이지 번호(PPN)를 적어 둔 표가 L2P 매핑 테이블이다.

L2P 매핑 테이블 (DRAM) LPNPPN 0B0 · P0 1B0 · P1 2 B0·P2 B1·P1 3B0 · P3 4B1 · P0 항목 1개 ≈ 4 B 4 KB마다 1개 호스트: LPN 2 쓰기 (새 데이터 C′) 블록 B0 블록 B1 (쓰기 중) P0 · LPN0 (A) P1 · LPN1 (B) P2 · LPN2 (C) 무효 P3 · LPN3 (D) P0 · LPN4 (E) P1 · LPN2 (C′) P2 · 빈 P3 · 빈 ① 빈 페이지에 새로 씀 ② 옛 페이지는 '무효' 표시 ③ 테이블 항목 갱신
그림 10-4. Out-of-place 업데이트. LPN 2를 고쳐 쓰면 새 데이터 C′는 현재 쓰는 중인 블록 B1의 다음 빈 페이지로 가고, 매핑 테이블이 B1·P1을 가리키도록 바뀐다. B0·P2의 옛 데이터 C는 지울 수 없으니 무효 페이지(stale)로 남는다. 초록 = 유효, 빨강 = 무효.

이 방식 덕분에 모든 쓰기는 "빈 페이지에 순서대로 쓰기"가 되어 빠르고, 소거는 쓰기 경로에서 빠진다. 대가는 두 가지다. 첫째, 무효 페이지가 계속 쌓여 언젠가 치워야 한다(다음 절의 가비지 컬렉션). 둘째, 모든 LPN의 위치를 담은 큰 테이블을 빠른 메모리에 둬야 한다.

$$ M_{L2P} = \frac{C}{S_{map}} \times E \;\;\approx\;\; \frac{1\ \text{TB}}{4\ \text{KB}} \times 4\ \text{B} = 2.4\times 10^{8} \times 4\ \text{B} \approx 1\ \text{GB} $$
여기서 \(C\)는 용량, \(S_{map}\)은 매핑 단위(보통 4 KB, 파일 시스템 블록 크기와 일치), \(E\)는 항목 크기. 32비트 항목은 \(2^{32}\)개 × 4 KB = 16 TB까지 가리킬 수 있어 그 이상은 항목이 더 커진다. 매핑 단위가 4 KB면 DRAM은 용량의 약 1/1000.

그래서 30 TB급 데이터센터 SSD는 DRAM이 30 GB 이상 필요해진다. 2023~2026년 QLC 기반 60~245 TB급 제품이 나오면서 매핑 단위를 16 KB 이상으로 키워 DRAM을 1/4 이하로 줄이는 설계(Indirection Unit, IU 확대)가 널리 쓰이기 시작했다. 대신 4 KB 랜덤 쓰기가 들어오면 16 KB 단위를 읽고-고치고-다시 써야(read-modify-write) 하므로 쓰기 증폭이 커진다.

SIMULATOR

매핑 테이블 DRAM 요구량 계산기

매핑 단위 (IU)
매핑 항목 수—
항목 크기 (물리 주소 비트)—
L2P 테이블 크기—
HMB가 덮는 LBA 범위—
물리 주소 비트 = ⌈log₂(물리 페이지 수)⌉ (OP 10% 포함 가정), 항목 크기는 바이트 단위로 올림. 해볼 것: ① 16 TB를 넘기면 항목이 4 B → 5 B로 커지는 계단을 찾아보자. ② 64 TB에서 IU를 4 KB → 16 KB로 바꾸면 DRAM이 몇 GB 줄어드는가? ③ HMB 64 MB로 4 KB 매핑이면 LBA 몇 GB 범위를 캐싱할 수 있는가(= DRAM-less SSD의 '빠른 구간').
페이지 매핑 · 블록 매핑 · 하이브리드

초기(2000년대) USB 메모리와 SD 카드는 DRAM이 없어 블록 단위로만 매핑했다(블록 매핑: 테이블이 수백 분의 1로 작아지지만 랜덤 쓰기가 끔찍하게 느리다). 그 사이에 로그 블록 몇 개만 페이지 매핑하는 하이브리드 FTL(BAST, FAST 등)이 연구되었다. 오늘날 SSD는 거의 모두 페이지 매핑이며, DRAM이 부족하면 테이블 전체를 NAND에 두고 일부만 캐싱하는 DFTL(Demand-based FTL) 방식을 쓴다. HMB SSD가 바로 이 구조다.

가비지 컬렉션과 쓰기 증폭

Out-of-place 업데이트를 계속하면 빈 블록은 줄고 무효 페이지만 늘어난다. 빈 블록이 일정 수 아래로 떨어지면 FTL은 가비지 컬렉션(GC, Garbage Collection)을 시작한다. ① 희생 블록(victim)을 고르고, ② 그 안의 유효 페이지를 다른 빈 페이지로 복사하고(매핑 테이블도 갱신), ③ 희생 블록을 소거해 빈 블록 목록에 되돌린다.

① 희생 블록 선택 ② 유효 페이지 복사 ③ 소거 VVV 유효 3 / 8 → u = 0.375 빈 블록 (GC 대상 스트림) 복사 3회 = 추가 쓰기 소거됨 빈 페이지 8 P/E 카운트 +1
그림 10-5. 가비지 컬렉션. 8페이지 중 3페이지가 유효한 블록을 치우면 3번의 복사(NAND 쓰기)를 치르고 5페이지 분량의 새 공간을 얻는다. 호스트는 이 5페이지를 쓸 수 있으므로, 호스트 5페이지 쓰기당 NAND에는 8페이지가 쓰인 셈이다.

GC가 하는 복사는 호스트가 요청하지 않은 추가 쓰기다. 그래서 SSD가 실제 NAND에 쓴 양은 호스트가 쓴 양보다 크다. 이 비율을 쓰기 증폭 계수(WAF, Write Amplification Factor)라 한다. 정상 상태에서 희생 블록의 평균 유효 비율이 \(u\)라면, 블록 하나를 치울 때마다 \(uP\)번 복사하고 \((1-u)P\)만큼의 새 공간을 얻으므로

$$ \mathrm{WAF} \;=\; \frac{\text{NAND 쓰기량}}{\text{호스트 쓰기량}} \;=\; \frac{(1-u)P + uP}{(1-u)P} \;=\; \frac{1}{1-u} $$
여기서 \(P\)는 블록당 페이지 수. 희생 블록이 비어 있을수록(\(u \to 0\)) WAF는 1에 가깝고, 꽉 찬 블록을 치우면(\(u \to 1\)) 폭발한다. 그림 10-5의 예는 \(u = 3/8\)이므로 WAF = 1.6.

그렇다면 희생 블록의 \(u\)를 낮추려면? 전체 물리 공간 중 호스트가 쓸 수 있는 부분을 줄여 여유 공간을 늘리면 된다. 이것이 오버 프로비저닝(OP, Over-Provisioning)이다. 512 GB 제품에 512 GiB(≈550 GB)의 NAND를 넣으면 약 7%가 호스트에 보이지 않는 여유 공간이 된다. 데이터센터용 '혼합 사용' 제품은 3.84 TB 대신 3.2 TB로 팔아 OP를 28% 안팎으로 키운다.

$$ \rho_{OP} = \frac{C_{phys} - C_{user}}{C_{user}}, \qquad \mathrm{WAF}_{greedy} \;\approx\; \frac{1 + \rho_{OP}}{2\rho_{OP}} \quad (\text{균일 랜덤 쓰기 근사}) $$
균일 랜덤 쓰기와 greedy 정책에 대한 널리 쓰이는 근사식. \(\rho\) = 7%면 약 7.6, 28%면 약 2.3이다. 실제 값은 블록 크기, 열린 블록 수, 워크로드의 핫/콜드 편중에 따라 달라지며 아래 시뮬레이터로 직접 확인할 수 있다.

희생 블록 선택 정책도 WAF를 좌우한다. Greedy는 유효 페이지가 가장 적은 블록을 고른다 — 당장 복사량이 최소다. Cost-Benefit은 로그 구조 파일 시스템(LFS, Rosenblum & Ousterhout 1992)에서 온 정책으로, 얻는 공간 \((1-u)\)과 데이터의 나이(age, 마지막으로 수정된 뒤 흐른 시간)를 함께 본다.

$$ \text{score}_{CB} = \frac{(1-u)\cdot \text{age}}{2u} $$
분모의 \(2u\)는 유효 페이지를 읽고(\(u\)) 쓰는(\(u\)) 비용. 오래 안 바뀐 블록은 '콜드' 데이터라 한번 옮기면 한동안 다시 무효화되지 않으므로, 유효율이 조금 높더라도 치워 두는 편이 장기적으로 이득이다.
SIMULATOR

FTL · 가비지 컬렉션 시뮬레이터

워크로드
희생 블록 정책
웨어 레벨링
유효 무효 빈 페이지 열린 블록 방금 GC한 블록 아래 띠 = 블록별 소거 횟수 (진할수록 많음)
호스트 쓰기 (드라이브 채움 횟수)—
NAND 쓰기—
WAF 누적—
WAF 최근 (정상 상태)—
근사식 (1+ρ)/2ρ—
소거 횟수 최소/평균/최대—
블록 64개 × 페이지 64개(총 4,096페이지)의 축소 모델. 한 열이 한 블록이다. 빈 블록이 2개 아래로 떨어지면 GC가 돈다. 블록이 작고 열린 블록·예비 블록이 여유 공간 일부를 차지하므로 정상 상태 WAF는 근사식보다 다소 높게 나온다. 해볼 것: ① 빈 드라이브에서 균일 랜덤 쓰기 — 처음 한 번 채울 때까지 WAF = 1, 그 뒤 GC가 시작되며 급상승한다. ② OP를 7% → 28%로 올리면 정상 상태 WAF가 얼마나 줄어드는가? 근사식과 비교하자. ③ 핫/콜드 워크로드에서 Greedy와 Cost-Benefit, 스트림 분리 on/off를 비교하자. ④ 웨어 레벨링 '없음'으로 핫/콜드를 오래 돌리면 소거 횟수가 블록마다 크게 벌어진다. '동적+정적'은 이를 평탄하게 만들지만 WAF가 약간 오른다. ⑤ 정상 상태에서 TRIM 25%를 누르면 유효 데이터가 줄어 실질 OP가 커지고 WAF가 떨어진다.
새 SSD의 벤치마크는 믿지 마라

공장에서 갓 나온(FOB, Fresh-Out-of-Box) SSD는 모든 블록이 비어 있어 GC가 없다. 랜덤 쓰기를 계속 퍼부으면 몇 분~몇 시간 뒤 GC가 시작되며 성능이 1/3~1/10로 떨어지고 요동친 뒤 정상 상태(steady state)에 안착한다. SNIA의 SSD 성능 시험 규격(PTS)이 드라이브를 두 번 가득 채우는 프리컨디셔닝을 요구하는 이유다. 데이터센터 SSD 스펙의 '4K 랜덤 쓰기 IOPS'는 정상 상태 값이다.

웨어 레벨링과 내구성: TBW · DWPD

P/E 수명이 3,000회라도, 특정 블록만 계속 소거되면 그 블록이 먼저 죽는다. 웨어 레벨링(Wear Leveling)은 모든 블록이 비슷한 속도로 닳도록 하는 기법이다.

완벽한 웨어 레벨링을 가정하면, SSD 전체가 견딜 수 있는 호스트 쓰기 총량은 NAND 전체 용량 × P/E 수명을 WAF로 나눈 값이다. 제조사가 보증하는 TBW(Terabytes Written)와, 보증 기간 동안 매일 용량의 몇 배를 쓸 수 있는지 나타내는 DWPD(Drive Writes Per Day)는 다음과 같이 연결된다.

$$ \mathrm{TBW} \approx \frac{C_{NAND} \times N_{PE}}{\mathrm{WAF}},\;\; C_{NAND} \approx C(1+\rho_{OP}), \qquad \mathrm{DWPD} = \frac{\mathrm{TBW}}{C \times 365 \times Y_{warranty}}, \qquad \text{수명(년)} = \frac{\mathrm{TBW}}{W_{day} \times 365} $$
\(C\)는 사용자 용량(\(C_{NAND}\)는 OP를 포함한 실제 NAND 용량), \(N_{PE}\)는 P/E 수명, \(Y\)는 보증 연수, \(W_{day}\)는 하루 호스트 쓰기량. 제조사 정격 TBW는 JEDEC JESD218/219의 워크로드·데이터 보존 조건(예: 소비자용은 수명 말기에도 전원 없이 30℃에서 1년 보존)을 만족하도록 여유를 두고 매긴다. 기업용은 보존 요구가 짧아(예: 40℃에서 3개월) 같은 NAND라도 더 많은 P/E까지 쓸 수 있다.
제품 등급 (예)용량정격DWPD (5년)비고
소비자 TLC (PCIe 4.0 플래그십)1 TB600 TBW≈ 0.33일반 PC 사용(하루 20~50 GB)으로 수십 년
소비자 QLC2 TB~ 400–700 TBW≈ 0.1–0.2읽기 위주 대용량
데이터센터 읽기 집중형 (TLC)3.84 TB1 DWPD1≈ 7 PBW, OP ~7%
데이터센터 혼합 사용 (TLC)3.2 TB3 DWPD3같은 NAND, OP ~28% → WAF↓
데이터센터 대용량 QLC (2024~)30~245 TB~0.3–0.6 DWPD0.3–0.6순차 쓰기 위주, 16 KB+ IU
SIMULATOR

내구성 계산기: TBW · DWPD · 수명

오버 프로비저닝
보증 기간
TBW (이상적)—
DWPD (보증 기간)—
NAND 실제 쓰기 / 일—
예상 수명—
그래프: 누적 호스트 쓰기(청록 선)가 TBW(주황 수평선)에 닿는 시점이 이론 수명이다. 해볼 것: ① 소비자 TLC 1 TB · 하루 50 GB면 수명이 보증 기간의 몇 배인가? ② 서버 혼합 사용 프리셋에서 WAF를 1.5 → 5로 바꾸면 DWPD가 어떻게 되는가 — OP로 WAF를 낮추는 것이 내구성의 핵심 레버다. ③ QLC(P/E 1,000)에 하루 2 TB를 쓰는 로그 서버는 몇 년을 버티는가?
수명 끝에서는 무슨 일이?

블록이 닳으면 프로그램/소거가 실패하거나 ECC로 못 고치는 오류가 나는데, 컨트롤러는 그 블록을 배드 블록으로 표시하고 예비 블록으로 대체한다. 예비 블록(OP의 일부)이 바닥나면 SSD는 데이터 손실을 막기 위해 읽기 전용 모드로 들어간다. NVMe의 SMART 로그에 있는 'Percentage Used'와 'Available Spare' 항목으로 이 상태를 미리 볼 수 있다. 실제로는 정격 TBW를 몇 배 넘겨도 동작하는 경우가 많지만, 보존 기간(전원 없이 데이터를 유지하는 시간)이 짧아진다(11장).

SLC 캐시와 TRIM

TLC 셀에 3비트를 쓰려면 8개 \(V_t\) 레벨을 정밀하게 맞추느라 여러 번의 프로그램-검증 펄스가 필요하다(8장의 ISPP). 반면 같은 셀을 1비트(SLC 모드)로 쓰면 두 레벨만 가르면 되므로 수 배 빠르다. 거의 모든 소비자용 SSD는 이 점을 이용해 빈 공간 일부를 SLC 캐시(SLC write buffer)로 쓴다. 호스트 쓰기를 일단 SLC 모드로 빠르게 받고, 유휴 시간에 3개 SLC 블록의 데이터를 1개 TLC 블록으로 옮겨 담는다(폴딩, folding).

호스트 쓰기 빠름 SLC 캐시 영역 1 bit/셀 · t_PROG ~0.1–0.2 ms S1S2S3 빈 공간의 일부를 빌림 (동적: 드라이브가 찰수록 축소) 폴딩 유휴 시 TLC 영역 3 bit/셀 · t_PROG ~0.5 ms T = S1 + S2 + S3 캐시가 꽉 차면 호스트 쓰기가 여기로 직접 (느림) 캐시 소진 후 경로 → 속도 절벽 SLC 1개 = TLC 1/3 공간 효율 ↓
그림 10-6. SLC 캐시. SLC 모드는 같은 셀에 1비트만 저장하므로 3배의 공간을 쓰지만 쓰기가 빠르다. 캐시가 빈 동안은 PCIe 링크 속도에 가깝게 쓰다가, 캐시가 다 차면 느린 TLC(또는 QLC) 직접 쓰기로 떨어진다 — 쓰기 속도 절벽(write cliff).

캐시 크기는 제품마다 다르다. 몇 GB 크기로 고정된 정적 SLC 캐시와, 빈 공간에 비례해 수십~수백 GB까지 늘어나는 동적 SLC 캐시를 섞어 쓰는 경우가 많다. 동적 캐시는 드라이브가 찰수록 작아지므로, 80~90% 찬 SSD에 큰 파일을 복사하면 절벽이 훨씬 일찍 온다. QLC SSD는 캐시 밖 쓰기 속도가 HDD 수준(수백 MB/s 이하)까지 떨어지기도 한다.

SIMULATOR

SLC 캐시와 쓰기 속도 절벽

용량
NAND
캐시 방식
현재 SLC 캐시 크기—
캐시 안 / 밖 속도—
절벽까지 시간—
총 소요 시간—
평균 속도—
교육용 모델: '동적 + 정적' 캐시 ≈ 빈 공간 ÷ (셀당 비트 수) × 0.75 + 정적 캐시(용량 TB당 약 6 GB), '정적만'은 용량 TB당 약 24 GB, 캐시 안 속도 ≈ 6.5 GB/s(Gen4), 캐시 밖은 다이 수에 비례(실제 값은 제품마다 크게 다르다). 쓰기량은 빈 공간보다 클 수 없다. 해볼 것: ① 2 TB TLC에서 사용률 20% → 85%로 올리며 절벽 위치를 보자. ② QLC로 바꾸면 절벽 뒤 속도가 얼마나 떨어지는가? ③ 정적 캐시만 쓰는 설계는 어떤 사용자에게 유리할까(찬 드라이브에서도 일정한 성능).

TRIM: 지운 파일을 SSD에 알려 주기

파일 시스템이 파일을 지워도, 전통적으로는 메타데이터만 고치고 데이터 블록은 그대로 둔다. HDD는 상관없지만 SSD 입장에서는 그 LBA들이 여전히 유효 데이터로 보이므로, GC 때 쓸데없이 복사된다. TRIM(ATA) 또는 NVMe의 Deallocate(Dataset Management 명령)는 "이 LBA 범위는 더 이상 안 쓴다"고 SSD에 알려 주는 명령이다. FTL은 해당 매핑을 지우고 그 물리 페이지를 무효로 표시한다. 효과는 유효 데이터가 줄어드는 것 — 즉 실질 오버 프로비저닝이 늘어나 WAF가 낮아지는 것이다. 앞의 GC 시뮬레이터에서 TRIM 25% 버튼으로 확인할 수 있다. 리눅스의 fstrim, 윈도우의 '드라이브 최적화'가 주기적으로 TRIM을 보낸다.

실용 팁: SSD를 100% 채우지 말자

파티션에 할당하지 않은 공간이나 TRIM된 빈 공간은 FTL 입장에서 추가 OP다. 소비자용 SSD라도 10~20%를 비워 두면 동적 SLC 캐시가 넉넉하게 유지되고 GC 효율이 좋아져 성능과 수명이 모두 개선된다. 데이터센터에서 3.84 TB 드라이브를 3.2 TB로 파티션해(또는 NVMe namespace를 작게 만들어) 혼합 사용 등급처럼 쓰는 것도 같은 원리다.

NVMe와 PCIe: 병렬성을 위한 인터페이스

SATA SSD는 HDD용으로 설계된 AHCI 프로토콜을 썼다. AHCI는 명령 큐가 1개, 깊이 32이고, 명령 하나를 내는 데 여러 번의 레지스터 접근(비캐시 MMIO 읽기)과 락이 필요했다. 회전 원판의 지연이 수 ms이던 시절엔 문제가 없었지만, 수십 µs에 응답하고 수십 개 다이가 동시에 일하는 SSD에는 병목이었다. NVMe(Non-Volatile Memory Express, 2011년 1.0)는 처음부터 플래시와 멀티코어 CPU를 위해 설계되었다.

호스트 (CPU 코어 + 메인 메모리) 코어 0 SQ0 CQ0 코어 1 SQ1 CQ1 ⋮⋮ 코어마다 큐 쌍 (락 불필요) 코어 N SQn CQn SQ 항목 64 B · CQ 항목 16 B I/O 큐 최대 65,535개 × 깊이 최대 65,536 NVMe 컨트롤러 (SSD) 도어벨 레지스터 SQ tail · CQ head 명령 인출 · 실행 FTL → NAND 다이들 MSI-X 인터럽트 ② SQ 도어벨 ③ 명령 인출 (DMA) ⑤ 완료 기록 ⑥ 인터럽트 ① 명령 기록
그림 10-7. NVMe 큐 구조. 코어마다 제출 큐(SQ)와 완료 큐(CQ)가 메인 메모리에 링 버퍼로 있다. ① 호스트가 SQ에 64바이트 명령을 쓰고 ② SSD의 도어벨 레지스터에 새 tail 위치를 한 번 기록하면, ③ 컨트롤러가 DMA로 명령을 가져가 ④ 실행하고 ⑤ CQ에 완료 항목을 쓴 뒤 ⑥ 인터럽트를 보낸다. 호스트는 완료를 처리하고 CQ head 도어벨을 갱신한다. 명령 하나에 MMIO 쓰기 두 번이면 충분하다.

코어마다 전용 큐 쌍이 있으니 코어끼리 락을 잡을 필요가 없고, 큐 깊이가 깊어 수십~수백 개의 명령을 동시에 SSD 안으로 밀어 넣을 수 있다. 이 동시 명령들이 2절의 다이 병렬성을 채운다. 큐 깊이(QD), IOPS, 지연 사이의 관계는 대기행렬 이론의 리틀의 법칙(Little's law)이 정확히 말해 준다.

$$ \text{QD} = \text{IOPS} \times \text{지연} \qquad\Longrightarrow\qquad \text{IOPS} = \frac{\text{QD}}{\text{지연}} $$
시스템 안에 머무는 평균 요청 수 = 도착률 × 평균 체류 시간. 4 KB 랜덤 읽기 지연이 80 µs인 SSD에 QD 1로 요청하면 12,500 IOPS(≈ 50 MB/s)에 불과하다. 수십만~백만 IOPS는 QD를 수십~수백으로 높여 다이들을 동시에 바쁘게 할 때만 나온다. 다이가 포화되면 QD를 더 올려도 IOPS는 늘지 않고 지연만 늘어난다.
PCIe 세대전송률/레인인코딩×4 단방향 대역폭대표 SSD 순차 읽기SSD 보급 시기
Gen38 GT/s128b/130b NRZ≈ 3.9 GB/s~3.5 GB/s2015~
Gen416 GT/s128b/130b NRZ≈ 7.9 GB/s~7.0–7.4 GB/s2019~
Gen532 GT/s128b/130b NRZ≈ 15.8 GB/s~12–14.5 GB/s2023~
Gen664 GT/sPAM4 + FLIT (FEC)≈ 30 GB/s~28 GB/s (데이터센터용)2026~ (데이터센터용부터)

PCIe 6.0은 6장에서 본 PAM4 신호를 도입해 레인당 64 GT/s를 얻고, 오류율 증가를 FEC와 고정 크기 FLIT 패킷으로 감당한다. 참고로 SATA 3의 한계는 600 MB/s(실효 ~550 MB/s)다.

SIMULATOR

큐 깊이 vs IOPS · 지연 (리틀의 법칙)

병렬 다이 수
호스트 링크 (×4)
4 KB 랜덤 읽기 IOPS—
대역폭—
평균 지연—
바쁜 다이 (평균)—
병목—
모델: QD개의 요청이 D개 다이에 무작위로 흩어지면 평균 D(1−(1−1/D)QD)개 다이가 바쁘다. IOPS = min(바쁜 다이/tR, QD/(tR+오버헤드), 컨트롤러 한계 ~2.5 M, 링크/4 KB), 지연 = QD/IOPS. 해볼 것: ① QD 1의 IOPS가 1/(tR+오버헤드)와 같은지 확인. ② 다이 32개에서 QD를 올리면 어디서 포화되는가? 그 뒤 지연은 QD에 비례해 늘어난다. ③ tR를 3~10 µs(저지연 SLC급 NAND)로 낮추면 병목이 컨트롤러/링크로 옮겨 간다.

ZNS와 FDP: 호스트와 FTL의 협업

지금까지 본 FTL은 호스트에게 NAND의 모든 사정을 숨기는 '블랙박스'였다. 그 대가가 쓰기 증폭, OP로 버리는 용량, 큰 DRAM, 그리고 GC가 도는 순간 튀는 꼬리 지연(tail latency)이다. 문제의 뿌리는 FTL이 데이터의 수명을 모른다는 데 있다. 곧 지워질 임시 파일과 몇 년 갈 아카이브가 같은 블록에 섞이면, 그 블록은 GC 때 반드시 유효 데이터를 복사해야 한다. 데이터를 가장 잘 아는 호스트가 배치를 거들면 이 문제가 크게 줄어든다.

기존 블록 인터페이스 ZNS (Zoned Namespace) FDP (Flexible Data Placement) 호스트: 아무 LBA나 쓰기 FTL (페이지 매핑, GC) 수명이 다른 데이터가 섞임 WAF 2~5, OP 7~28% 호스트: 존 단위 순차 쓰기 얇은 매핑 (존 → 블록) 존0존1존2 호스트가 존을 통째로 리셋 장치 WAF ≈ 1, OP·DRAM ↓ 호스트: 쓰기에 배치 힌트 태그 FTL 유지 + 핸들별 분리 핸들A핸들B핸들C 같은 수명끼리 같은 블록 하위 호환, WAF → 1 근처
그림 10-8. 호스트–장치 협업 인터페이스. ZNS(NVMe 2.0, 2021)는 LBA 공간을 순차 쓰기만 허용하는 '존'으로 나누고, 존 리셋을 호스트가 책임진다 — 블록 소거와 1:1로 맞아떨어지므로 장치 GC가 사라진다. FDP(NVMe TP4146, 2022년 비준)는 기존 FTL을 그대로 두되, 쓰기 명령에 '배치 핸들'을 붙여 같은 핸들 데이터끼리 같은 블록(Reclaim Unit)에 모으게 한다.

ZNS는 이론상 가장 깔끔하지만 호스트 소프트웨어(파일 시스템, RocksDB 같은 KV 저장소)가 순차 쓰기 규칙을 지키도록 다시 짜야 한다. SMR 하드디스크를 위한 zoned 인터페이스와 같은 계열이라 리눅스의 F2FS, btrfs 등이 지원한다. FDP는 태그를 모르는 호스트에서도 평범한 SSD처럼 동작하는 하위 호환성 덕분에 2024년 이후 하이퍼스케일러(Meta, Google 등)가 주도해 도입이 빨라지고 있다. 두 방식 모두 핵심은 5절의 교훈 — 수명이 비슷한 데이터를 같은 블록에 모으면 희생 블록의 \(u\)가 0에 가까워지고 WAF는 1에 가까워진다 — 을 호스트의 지식으로 실현하는 것이다.

컴퓨테이셔널 스토리지와 CXL

SSD 컨트롤러는 이미 여러 개의 CPU 코어를 가진 컴퓨터다. 데이터를 호스트로 옮기지 않고 SSD 안에서 필터링·압축·검색하는 컴퓨테이셔널 스토리지, 그리고 CXL 인터페이스로 SSD를 바이트 단위로 접근하는 메모리 확장 장치처럼 쓰려는 시도가 진행 중이다. 12장의 PIM(Processing-in-Memory)과 같은 문제 의식 — 데이터 이동이 계산보다 비싸다 — 에서 출발한다.

핵심 정리

  1. SSD는 컨트롤러(Arm 코어, LDPC ECC, 8~16 채널), 여러 NAND 다이, 매핑 테이블용 DRAM(또는 HMB)으로 구성된다. 속도는 느린 다이 수십 개를 채널·웨이·플레인으로 병렬 구동해 얻는다.
  2. 다이당 쓰기 처리량은 \(N_{plane} S_{prog}/(t_{xfer}+t_{PROG})\) — 수백 MB/s에 불과하므로 같은 컨트롤러라도 다이 수(용량)가 많을수록 빠르다. 읽기는 채널 버스가 병목이 되기 쉽다.
  3. NAND는 덮어쓸 수 없고(erase-before-write), 쓰기 단위(페이지 16 KB)와 소거 단위(블록 수 MB~수십 MB)가 다르며, P/E 수명이 유한하다(TLC 약 1k~3k회).
  4. FTL은 out-of-place 업데이트와 L2P 페이지 매핑으로 이를 숨긴다. 4 KB 매핑 × 4 B 항목이면 DRAM은 용량의 약 1/1000(1 TB당 1 GB). 대용량 QLC는 매핑 단위를 16 KB 이상으로 키운다.
  5. 가비지 컬렉션은 희생 블록의 유효 페이지를 복사한 뒤 소거한다. 정상 상태 WAF = 1/(1−u)이며, 오버 프로비저닝이 클수록 u가 낮아져 WAF가 줄어든다(균일 랜덤·greedy 근사 (1+ρ)/2ρ).
  6. Greedy는 당장 복사량을 최소화하고, Cost-Benefit은 데이터 나이를 고려해 핫/콜드 편중 워크로드에서 유리하다. 스트림 분리(ZNS·FDP 포함)는 수명이 비슷한 데이터를 모아 WAF를 1에 가깝게 한다.
  7. 동적/정적 웨어 레벨링으로 소거를 고르게 분산한다. TBW ≈ CNAND·NPE/WAF, DWPD = TBW/(C·365·Y). TRIM은 무효 데이터를 알려 실질 OP를 늘리고 WAF를 낮춘다.
  8. SLC 캐시는 쓰기를 1비트 모드로 빠르게 받지만 소진되면 쓰기 속도 절벽이 온다. NVMe는 코어별 다중 큐(최대 64K × 64K)로 병렬성을 끌어내며, 리틀의 법칙 IOPS = QD/지연이 성능을 지배한다.

확인 퀴즈

1. NAND 플래시에서 이미 쓴 페이지를 제자리에서 덮어쓸 수 없는 근본적인 이유는?

프로그램은 비트를 1→0(Vt 상승)으로만 바꿀 수 있다. 0→1로 되돌리려면 웰에 고전압을 거는 소거가 필요하고, 이 고전압은 블록 전체에 걸린다. 그래서 FTL은 out-of-place 업데이트를 한다.

2. 4 TB SSD가 4 KB 페이지 매핑, 항목당 4 B를 쓴다면 L2P 테이블에 필요한 DRAM은 약 얼마인가?

항목 수 = 4 TB / 4 KB ≈ 109개, × 4 B ≈ 4 GB. 용량의 약 1/1000이라는 경험칙과 같다.

3. 정상 상태에서 GC가 고르는 희생 블록의 평균 유효 페이지 비율이 25%라면 쓰기 증폭 계수(WAF)는?

WAF = 1/(1−u) = 1/0.75 ≈ 1.33. 블록 하나를 비울 때 0.25P를 복사하고 0.75P의 새 공간을 얻으므로, 호스트 0.75P 쓰기당 NAND에 P가 쓰인다.

4. 2 TB SSD의 NAND P/E 수명이 3,000회, WAF가 2.5일 때, 이상적 웨어 레벨링을 가정하고 OP는 무시한 5년 기준 DWPD는?

TBW = 2 TB × 3000 / 2.5 = 2,400 TB. DWPD = 2400 / (2 × 365 × 5) ≈ 0.66. 실제 제품 정격은 보존 조건 등의 여유 때문에 이보다 낮게 매겨진다.

5. 4 KB 랜덤 읽기 평균 지연이 100 µs로 유지되는 구간에서 큐 깊이 32로 요청하면 IOPS는 약 얼마인가?

리틀의 법칙: IOPS = QD / 지연 = 32 / 100 µs = 320,000. 대역폭으로는 약 1.3 GB/s다. 다이가 포화되면 QD를 더 올려도 IOPS는 그대로이고 지연만 늘어난다.

6. 운영체제가 삭제된 파일의 LBA에 대해 TRIM(Deallocate)을 보내면 SSD 내부에서 일어나는 가장 중요한 효과는?

TRIM은 즉시 소거를 강제하지 않는다. 매핑을 지우고 페이지를 무효로 만들 뿐이며, 그 결과 GC 희생 블록의 유효 비율 u가 낮아져 WAF와 GC 부담이 줄어든다. P/E 카운트는 줄어들 수 없다.