SSD는 왜 펌웨어가 필요한가: NAND의 제약에서 출발하기
오늘의 질문
SSD는 HDD보다 빠르다. 여기까지는 누구나 안다. 그런데 조금만 들여다보면 이상한 점이 보인다.
HDD에는 이렇게 복잡한 소프트웨어가 없는데, SSD 안에는 왜 매핑 테이블이니 가비지 컬렉션이니 하는 것들이 돌아가고 있는가? 반도체라서 그냥 원하는 주소에 쓰면 되는 것 아닌가?
답은 정반대다. NAND 플래시는 HDD보다 다루기 까다로운 매체다. 원하는 자리에 그냥 쓸 수가 없다. SSD 펌웨어가 하는 일의 대부분은 이 제약을 호스트에게 숨기는 것이다. 이 글은 그 제약이 무엇이고, 펌웨어가 그걸 어떻게 감당하는지를 순서대로 따라간다.
먼저 답하기
세 문장으로 줄이면 이렇다.
NAND는 쓰기와 지우기의 단위가 다르다. 쓰기는 페이지 단위인데 지우기는 그보다 훨씬 큰 블록 단위여서, 이미 쓴 자리에 덮어쓸 수가 없다. 그래서 펌웨어는 새 데이터를 빈 자리에 쓰고 옛 자리를 무효로 표시한 뒤, “어느 논리 주소가 지금 어느 물리 위치에 있는지”를 기록한 지도를 갱신한다.
이 방식을 계속하면 무효 페이지가 쌓여 쓸 공간이 말라간다. 이걸 회수하는 청소 작업이 가비지 컬렉션이고, SSD 성능이 상황에 따라 들쭉날쭉한 이유의 상당 부분이 여기서 나온다.
주제에 맞는 핵심 설명
먼저 짚고 갈 오해: HDD 안에는 NAND가 없다
본론에 들어가기 전에 아주 흔한 오해 하나를 정리하고 간다.
HDD와 SSD는 같은 부품을 쓰는데 소프트웨어만 다른 것 아닌가?
아니다. 둘은 데이터를 담는 재료 자체가 다르다. HDD 안에는 NAND 플래시 칩이 들어 있지 않고, SSD 안에는 돌아가는 원판이 들어 있지 않다. 소프트웨어 차이가 아니라 물리적으로 다른 물건이다.
HDD 안에는 금속 원판(플래터)이 여러 장 겹쳐 있고, 이걸 고속으로 돌리는 모터가 있고, 그 위를 오가는 팔 끝에 헤드가 달려 있다. 원판 표면에는 자성 물질이 발려 있어서, 헤드가 그 위에 자기장을 걸어 자화 방향을 바꾸는 방식으로 0과 1을 기록한다. 카세트테이프나 비디오테이프와 원리가 같고, 다만 테이프 대신 원판을 쓰고 훨씬 정밀할 뿐이다. 핵심은 기계 장치라는 점이다.
SSD 안에는 회로기판 위에 NAND 플래시 칩이 여러 개 붙어 있고, 그 칩들을 지휘하는 컨트롤러 칩이 있고, 대개 DRAM도 하나 붙어 있다. 데이터는 칩 안의 아주 작은 셀에 전하를 가두는 방식으로 기록된다. 움직이는 부품이 하나도 없다.
왜 헷갈리는가
오해가 생기는 데는 이유가 있다. 겉에서 보이는 것이 정말로 똑같기 때문이다.
| 같아 보이는 것 | 실제로는 |
|---|---|
| 컴퓨터에 꽂는 케이블과 커넥터 | 인터페이스 규격만 공유할 뿐, 그 너머는 완전히 다르다 |
| 운영체제가 “C 드라이브”로 인식하는 방식 | 둘 다 “번호 매겨진 칸이 줄지어 있는 상자”인 척한다 |
읽기 / 쓰기 명령의 모양 |
명령은 같아도 내부에서 벌어지는 일이 전혀 다르다 |
운영체제 입장에서 두 장치는 번호를 대면 데이터를 꺼내주고 넣어주는 똑같은 상자로 보인다. 이 겉모습을 유지하는 것이 표준 인터페이스의 목적이다. 그래서 사용자는 안이 이렇게 다른 줄 모르고 쓰게 된다.
그럼 HDD에는 펌웨어가 없나
있다. 다만 하는 일이 다르다.
HDD 펌웨어는 모터 회전 속도를 제어하고, 헤드를 목표 트랙에 정확히 위치시키고, 읽기 오류가 반복되는 불량 섹터를 여분 영역으로 재할당하고, 내장 캐시를 관리한다. 전부 필요한 일이지만 주소 변환을 매번 하지는 않는다. 논리 주소와 물리 위치가 대체로 고정된 대응 관계를 유지하기 때문이다. 같은 자리에 몇 번이든 덮어쓸 수 있으니 위치를 옮길 이유가 없다.
SSD 펌웨어는 여기에 더해 덮어쓰기가 안 되는 매체를 덮어쓰기가 되는 것처럼 보이게 만드는 일을 해야 한다. 이 글 나머지가 전부 그 이야기다.
정리하면 이렇다. 펌웨어가 복잡해서 매체가 달라진 게 아니라, 매체가 달라서 펌웨어가 복잡해졌다. 인과의 방향이 이쪽이다.
참고로 예외가 하나 있다. HDD와 NAND를 한 장치에 함께 넣어 자주 쓰는 데이터를 NAND에 캐싱하는 하이브리드 제품(SSHD)이 존재했다. 다만 이건 두 매체를 한 상자에 담은 것이지, HDD가 NAND로 기록한다는 뜻은 아니다.
HDD와 SSD는 무엇이 다른가
두 매체의 차이를 “빠르다 / 느리다”로만 두면 뒤에 나올 이야기가 이해되지 않는다. 접근 방식 자체가 다르다는 점부터 봐야 한다.
HDD는 자성 물질을 입힌 원판을 돌리고 그 위를 헤드가 움직이며 읽고 쓴다. 1956년 IBM이 처음 상용화한 이래 기본 구조가 유지되고 있다. 원하는 위치에 헤드를 옮기는 데 시간이 걸리므로 위치가 흩어지면 느려지고, 이어져 있으면 빠르다. 대신 같은 자리에 몇 번이든 다시 쓸 수 있다. 자화 방향을 바꾸는 데 별도의 준비 동작이 필요 없기 때문이다.
SSD는 NAND 플래시 반도체에 전하를 가둬 데이터를 표현한다. 움직이는 부품이 없으니 위치에 따른 지연 차이가 거의 없다. 여기까지는 명백한 장점이다. 문제는 전하를 빼내는 동작(지우기)이 개별 위치 단위로 되지 않는다는 데 있다.
| 축 | HDD | SSD (NAND) |
|---|---|---|
| 저장 방식 | 자화 방향 | 셀에 가둔 전하 |
| 위치에 따른 지연 | 헤드 이동·회전 대기 있음 | 거의 없음 |
| 같은 자리 덮어쓰기 | 가능 | 불가능 |
| 쓰기 단위 | 섹터 | 페이지 |
| 지우기 단위 | 별도 개념 없음 | 블록 (페이지 여러 개 묶음) |
| 수명 제약 | 기계적 마모 | 셀의 P/E 사이클 한계 |
| 필요한 소프트웨어 | 단순 | 매핑·GC·웨어 레벨링 등 |
마지막 두 줄이 이 글의 주제다. SSD 펌웨어가 복잡한 이유는 성능을 더 짜내기 위해서가 아니라, 덮어쓰기가 안 되고 수명이 유한한 매체를 “아무 데나 덮어써도 되는 디스크”인 척 보이게 만들어야 하기 때문이다.
셀 하나를 들여다보기: 왜 덮어쓸 수 없는가
“덮어쓰기가 안 된다”는 말을 계속 썼는데, 왜 안 되는지를 먼저 짚어야 뒤의 이야기가 전부 이어진다. 이유는 펌웨어나 규격이 아니라 셀의 물리 구조에 있다.
쓰기는 전자를 넣는 동작이다
NAND 셀은 아주 작은 트랜지스터인데, 그 안에 전자를 가둬둘 수 있는 층이 하나 더 있다. 여기에 전자가 얼마나 들어 있느냐로 데이터를 표현한다.
전자가 들어가면 그 셀을 켜는 데 필요한 전압, 즉 문턱전압(Vt, threshold voltage)이 올라간다. 나중에 읽을 때는 특정 전압을 걸어보고 셀이 켜지는지 안 켜지는지를 보고 안에 전자가 얼마나 있는지를 역으로 판단한다.
쓰기(프로그램)는 이 전자를 밀어 넣는 동작이다. 그리고 여기서 결정적인 성질이 나온다. 밀어 넣는 것만 되고 빼내는 것은 안 된다.
NAND 플래시의 오류 특성을 정리한 Cai 외의 문헌은 이 점을 명확히 서술한다. 셀은 기존 데이터가 지워진 뒤에만 새 데이터로 다시 프로그램될 수 있으며, 이는 프로그램에 쓰이는 방식(ISPP)이 셀의 전압을 올리는 것만 가능하기 때문이다.
무슨 뜻인지 비유로 보면 이렇다. 컵에 물을 붓는 것만 가능하고 따라 버리는 기능은 없는 상황이다. 물이 절반 든 컵을 “물 없음” 상태로 만들 방법이 붓기 동작 안에는 없다. 이미 값이 쓰인 셀에 다른 값을 쓰려면 전자를 빼야 하는데, 프로그램 동작에는 그 기능 자체가 없다.
전자를 빼는 동작은 블록 전체를 건드린다
그러면 전자는 어떻게 빼는가. 이게 지우기(erase)다.
같은 문헌은 지우기 동작을 이렇게 설명한다. 셀의 컨트롤 게이트를 접지시키고 트랜지스터의 몸통, 즉 기판(substrate)에 높은 전압을 걸어 전자를 저장층에서 기판 쪽으로 뽑아낸다. 그 결과 블록 안 모든 셀의 문턱전압 상태가 지워진 상태로 초기화된다.
여기서 블록 단위인 이유가 나온다. 같은 문헌의 표현을 그대로 옮기면, 한 블록 안의 모든 셀이 공통의 트랜지스터 기판을 공유하기 때문에(즉 블록 안 모든 트랜지스터의 몸통이 서로 연결되어 있기 때문에) 블록은 통째로 지워져야 한다.
기판이 물리적으로 이어져 있으니 거기에 전압을 걸면 그 위에 얹힌 셀 전부가 함께 영향을 받는다. 페이지 하나만 골라 지우려면 셀 그룹마다 기판을 따로 떼어놓아야 하는데, 그러면 구조가 복잡해지고 집적도가 떨어진다. 용량 대비 가격이 전부인 매체에서 이 교환은 성립하기 어렵다.
그래서 남는 결론
정리하면 두 가지가 어긋나 있다.
- 쓰기: 페이지 단위로 가능하지만 한 방향으로만 간다 (전자를 넣기만)
- 되돌리기: 방향을 되돌릴 수는 있지만 블록 단위로만 가능하다 (기판을 공유하므로)
이 어긋남이 SSD 펌웨어가 존재하는 근본 이유다. 이 다음 절부터는 이 제약 위에서 무엇을 해야 하는지를 다룬다.
페이지와 블록: 단위가 어긋나 있다
여기서 두 용어를 정의하고 간다.
페이지(page)는 읽기와 쓰기의 최소 단위다. 한 번에 프로그램(기록)할 수 있는 덩어리를 말한다. 블록(block)은 지우기의 최소 단위이며, 페이지 여러 개가 묶여 있다.
NAND 플래시 특성을 정리한 FAU Erlangen-Nürnberg의 자료는 조사된 장치들에서 페이지 크기가 2 KB 또는 4 KB이고, 16개에서 128개의 페이지가 하나의 블록을 이룬다고 보고한다. 같은 자료는 쓰기 지연이 대체로 200~300 마이크로초 범위에 있다고 정리한다.
왜 이렇게 단위가 어긋나 있을까. 앞 절에서 본 그대로다. 프로그램은 워드라인 단위로 걸 수 있어 페이지 단위가 되지만, 지우기는 셀들이 공유하는 기판에 전압을 거는 방식이라 그 기판을 함께 쓰는 범위 전체에 적용된다. 그 범위가 곧 블록이다.
이 어긋남이 만드는 결과는 단순하다. 이미 데이터가 쓰인 페이지에 다시 쓰려면 그 페이지가 속한 블록 전체를 지워야 한다. 그런데 그 블록에는 아직 살아 있는 다른 페이지들이 함께 들어 있다. 페이지 하나 고치자고 블록 전체를 지울 수는 없다.
그래서 다른 자리에 쓰고 지도를 고친다
펌웨어의 선택은 제자리 갱신을 포기하는 것이다. 이를 out-of-place update라고 부른다. NAND 플래시 신뢰성을 다룬 Cai 외의 정리 문헌은 이 동작을 이렇게 기술한다. 호스트가 어떤 논리 주소에 쓰기를 요청하면 컨트롤러는 갱신된 데이터를 다른 물리 페이지에 기록하고, 이전 물리 페이지를 무효(invalid)로 표시한다. 제자리 갱신이 불가능하기 때문이다.
여기서 주소가 세 층으로 나뉜다.
- LBA(Logical Block Address): 호스트가 쓰는 주소다. 보통 512바이트 섹터 번호이며, 호스트는 이것만 알면 된다.
- LPN(Logical Page Number): 펌웨어가 관리하는 논리 페이지 번호다. NAND 페이지 크기에 맞춰 LBA 여러 개를 묶은 단위다.
- 물리 주소(PPN): 실제 위치다. 채널, 웨이, 다이, 블록, 페이지 같은 좌표로 표현된다.
LBA를 LPN으로 바꿀 때 나누는 숫자는 어디서 오는가. 두 단위의 크기 비율이다. 호스트가 쓰는 섹터가 512 B이고 NAND 페이지가 4 KiB라면 이렇게 된다.
4 KiB ÷ 512 B = 4096 ÷ 512 = 8
즉 LBA 8개가 모여야 페이지 하나가 된다. 그래서 LPN = LBA ÷ 8이다. 나머지는 페이지 안에서의 위치(오프셋)가 된다. LBA 803이라면 803 ÷ 8 = 100 나머지 3이므로, LPN 100의 네 번째 섹터 자리를 뜻한다.
이 8은 고정된 숫자가 아니다. 페이지가 16 KiB라면 16384 ÷ 512 = 32가 되고, 호스트가 4096 B 논리 섹터를 쓰는 장치(4Kn)에서 페이지도 4 KiB라면 비율이 1이 되어 나눌 필요가 없다. 중요한 건 숫자 8이 아니라 “호스트의 단위와 NAND의 단위가 다르므로 환산이 필요하다”는 사실이다.
여기서 실무적으로 하나가 따라 나온다. 호스트가 512 B만 고쳐 쓰겠다고 해도 NAND는 페이지보다 작게 쓸 수 없다. 그러면 펌웨어는 원래 페이지를 읽어와 그 512 B만 바꾼 뒤 페이지 전체를 새 자리에 다시 쓴다. 512 B 요청이 4 KiB 쓰기가 되는 셈이고, 이것도 쓰기 증폭의 한 갈래다.
이 셋을 잇는 것이 매핑 테이블이다. 위 문헌들이 L2P(Logical to Physical) 매핑이라 부르는 것으로, 표의 색인이 논리 주소이고 내용이 물리 위치다. 쓰기 한 번의 순서는 이렇게 흘러간다.
- 호스트가
write(LBA 800)을 보낸다. - 펌웨어가 LBA를 LPN으로 환산한다.
800 ÷ 8 = LPN 100. - 매핑 테이블에서 LPN 100의 현재 물리 위치를 조회한다.
- 미리 지워둔 빈 페이지를 골라 새 데이터를 프로그램한다.
- 기존 물리 페이지를 무효로 표시한다.
- 매핑 테이블을 갱신한다. LPN 100이 새 물리 위치를 가리키게 한다.
헷갈리기 쉬운 지점: 무엇이 옮겨간 것인가
여기서 자주 나오는 오해를 정리하고 간다.
A 번지에 쓰려다가 덮어쓰기가 안 되니까, A는 무효로 표시하고 다른 번지에 쓴다는 뜻인가?
절반은 맞고 절반은 틀리다. 정확히 말하면 이렇다.
호스트가 말한 주소는 끝까지 바뀌지 않는다. 호스트는 LBA 800에 썼고, 나중에 읽을 때도 LBA 800을 부른다. 호스트 입장에서 데이터는 계속 같은 자리에 있다. 옮겨간 것은 그 논리 주소가 가리키는 물리적 위치다.
| 쓰기 전 | 쓰기 후 | |
|---|---|---|
| 호스트가 부르는 주소 (LBA) | 800 | 800 (그대로) |
| 논리 페이지 번호 (LPN) | 100 | 100 (그대로) |
| 실제 데이터가 있는 물리 위치 | 블록2 / 페이지8 | 블록2 / 페이지10 (바뀜) |
| 옛 물리 위치의 상태 | 유효 | 무효 |
그래서 “다른 번지에 쓴다”는 표현은 물리 위치를 두고 하는 말일 때만 맞다. 논리 주소 기준으로는 아무것도 옮겨가지 않았다.
무효로 표시되는 대상도 마찬가지다. 무효가 되는 것은 LBA 800이 아니라 옛 데이터가 들어 있던 물리 페이지다. LBA 800은 여전히 살아 있고, 단지 새 위치를 가리킬 뿐이다.
이 구분이 중요한 이유는 매핑 테이블의 존재 이유가 바로 여기에 있기 때문이다. 호스트에게는 “주소가 고정된 세계”를 보여주고, 실제로는 “위치가 계속 바뀌는 세계”에서 동작한다. 그 두 세계를 이어주는 번역기가 매핑 테이블이다. 이게 없으면 호스트는 자기가 방금 쓴 데이터가 어디로 갔는지 알 방법이 없다.
왜 마지막에 갱신하는가. 순서를 뒤집어 테이블을 먼저 고치고 기록하다가 전원이 끊기면, 테이블은 아직 존재하지 않는 데이터를 가리키게 된다. 그 상태로 부팅하면 쓰레기 값을 정상 데이터로 읽어버린다. 기록이 완료된 뒤에 갱신하면, 중간에 끊겨도 테이블은 여전히 옛 데이터를 가리키므로 쓰기가 실패했을 뿐 데이터가 깨지지는 않는다. 실제 제품은 여기에 전원 손실 대비 처리를 더 얹지만, 기본 원칙은 이 순서다.
그리고 이 방식의 부작용이 하나 있다. 같은 파일을 여러 번 수정하면 그 조각들이 물리적으로 여기저기 흩어진다. 호스트가 보는 논리 주소는 연속인데 실제 위치는 그렇지 않다. HDD였다면 치명적이었을 이 흩어짐이 SSD에서는 큰 문제가 되지 않는데, 위치에 따른 지연 차이가 거의 없기 때문이다. 다만 흩어짐은 다른 형태로 대가를 치른다. 바로 다음에 나올 무효 페이지 누적이다.
무효 페이지가 쌓이면: 가비지 컬렉션
무효로 표시된 페이지는 지워지기 전까지 아무것도 쓸 수 없는 죽은 공간이다. 위 문헌은 한 블록 안에 무효 페이지가 섞여 있는 상태를 단편화(fragmentation)라 부르고, 이 상태가 쌓이면 SSD가 새로 쓸 페이지를 잃게 된다고 지적한다.
이 공간을 되찾는 작업이 가비지 컬렉션(GC)이다.
같은 문헌은 기본 GC 알고리즘을 네 단계로 정리한다. (1) 단편화가 심한 블록을 고르고, (2) 그 안의 유효 페이지를 새 블록으로 옮기면서 각각의 매핑을 갱신하고 원래 페이지를 무효로 표시하며, (3) 선택된 블록을 지우고, (4) 그 블록을 빈 블록 목록(free list)에 넣는다.
언제 시작하는지도 중요하다. 컨트롤러는 미리 지워둔 빈 블록을 목록으로 관리하다가, 호스트 쓰기가 이어지면서 빈 블록 수가 기준 아래로 떨어지면 GC를 발동한다. 지우기 동작이 느리기 때문에 이걸 쓰기 요청의 임계 경로에서 미리 빼두려는 설계다.
성능이 나빠지는 이유를 숫자로
GC의 2단계와 3단계는 호스트가 요청하지 않은 작업이다. 그런데 NAND의 채널과 다이를 실제로 점유한다. 그 사이 도착한 호스트 요청은 기다려야 한다. 이것이 SSD 성능이 상황에 따라 달라지는 주된 이유다.
얼마나 나빠지는지는 희생 블록에 유효 페이지가 얼마나 남아 있느냐로 결정된다. 계산해보면 관계가 뚜렷하게 나온다.
가정 — 블록당 페이지 128개(FAU 자료가 보고한 범위의 상한), 페이지 16 KiB로 잡아 블록 하나가 2 MiB다. 희생 블록의 유효 페이지 비율을 u라 한다.
식 — 블록 하나를 회수하면 (1−u) × 128개의 빈 페이지를 얻는데, 그러려면 u × 128개를 복사해야 한다. 따라서 빈 페이지 하나를 확보하는 데 드는 복사량은 u ÷ (1−u)이고, 호스트 페이지 1개를 쓰기 위한 총 물리 쓰기는 1 + u ÷ (1−u) = 1 ÷ (1−u)가 된다. 이 값을 쓰기 증폭(WAF)이라 부른다.
| 유효 비율 u | 복사할 페이지 | 확보되는 페이지 | 호스트 1페이지당 물리 쓰기 |
|---|---|---|---|
| 0.00 | 0 | 128 | 1.00 |
| 0.25 | 32 | 96 | 1.33 |
| 0.50 | 64 | 64 | 2.00 |
| 0.75 | 96 | 32 | 4.00 |
| 0.90 | 115 | 13 | 10.00 |
| 0.95 | 122 | 6 | 20.00 |
u가 0.5를 넘어가면서 급격히 나빠진다. 0.9에서는 호스트가 1페이지를 쓸 때마다 NAND에는 10페이지가 쓰인다. 대역폭의 90%가 청소에 쓰이는 셈이고, 쓰기 수명도 그만큼 빨리 소모된다.
여기서 두 가지가 따라 나온다. 첫째, 드라이브가 꽉 찰수록 무효 페이지 비율이 낮은 블록만 남으므로 u가 올라가고 성능이 급락한다. 오버프로비저닝(사용자에게 보여주지 않고 남겨두는 여유 공간)을 두는 이유가 이것이다. 둘째, 순차 쓰기는 GC 부담이 거의 없다. 블록을 처음부터 끝까지 채우고 통째로 무효화하면 u가 0에 가까워 복사할 것이 없기 때문이다. 앞서 인용한 쓰기 증폭 분석 문헌도 순차 쓰기 워크로드에서는 블록 전체가 무효가 되므로 데이터 이동 없이 회수된다고 정리한다.
적용 한계 — 위 계산은 희생 블록의 유효 비율이 일정하다고 가정한 단순 모델이다. 실제 WAF는 블록 선정 정책, 데이터의 갱신 빈도 분포(핫/콜드 분리), 오버프로비저닝 비율, 웨어 레벨링 정책에 따라 달라진다. 페이지 크기와 블록당 페이지 수도 제품마다 다르므로, 실제 값을 알려면 대상 장치의 구성을 넣어 다시 계산해야 한다.
zoned storage: 제약을 숨기지 말고 드러내기
지금까지의 이야기는 전부 펌웨어가 NAND의 제약을 호스트에게 숨기는 구조였다. 호스트는 아무 주소에나 덮어쓸 수 있다고 믿고, 그 대가를 매핑 테이블과 GC가 치른다.
여기서 발상을 뒤집은 것이 zoned storage다. 관련 자료를 모아둔 Zoned Storage 프로젝트 문서는 이를 주소 공간이 여러 존(zone)으로 나뉘고, 각 존이 일반 저장장치와 다른 쓰기 제약을 갖는 장치 부류로 정의한다. 핵심 제약은 두 가지다.
첫째, 존 안에서는 순차적으로만 쓸 수 있다. 이를 순차 쓰기 제약이라 부르며, 각 존은 다음 쓰기 위치를 가리키는 쓰기 포인터(write pointer)를 갖는다. 둘째, 존 안의 데이터는 직접 덮어쓸 수 없고, 덮어쓰려면 먼저 존 리셋 명령으로 비워야 한다.
위 그림의 아래쪽을 앞에서 본 NAND 블록 이야기와 겹쳐 보면 닮은 점이 바로 보인다. 순차로만 쓰기, 중간 덮어쓰기 불가, 비우려면 통째로 초기화. 존은 블록의 성질을 거의 그대로 인터페이스로 끌어올린 것이다.
눈치챘겠지만 이건 NAND 블록의 성질을 거의 그대로 인터페이스에 노출한 것이다. 숨기지 않고 드러낸 셈이다.
SSD에서 이 인터페이스를 규정한 것이 NVMe의 ZNS(Zoned Namespace)다. 같은 문서는 ZNS가 NVMe 2.0 규격의 일부로 공개되었고 호스트 소프트웨어와 장치 컨트롤러 사이의 기능 분담을 새로 나눈다고 설명한다. ZNS 장치는 용량을 존 단위로 노출하며, 읽기는 임의 순서로 가능하지만 쓰기는 순차적이어야 한다. 존의 크기는 제조 시점에 고정되며 사용자가 바꿀 수 없다.
무엇을 얻는가. 같은 문서는 SSD가 존 인터페이스를 구현하는 이유로 쓰기 증폭 감소, 장치의 DRAM 필요량 감소, 대규모 환경에서의 서비스 품질 개선을 든다. 위에서 계산한 내용에 비춰보면 이유가 분명하다. 호스트가 순차로만 쓰도록 강제되면 블록이 통째로 무효화되는 패턴이 자연스럽게 만들어져 u가 낮아지고, 장치가 관리할 매핑의 세밀함이 줄어들면 매핑 테이블을 담을 DRAM도 줄어든다.
대가도 분명하다. 부담이 호스트로 넘어간다. 파일 시스템이나 응용이 존의 상태를 알고 순차 쓰기를 지켜야 한다. 같은 문서는 실무적인 함정도 짚는데, NVMe 규격상 컨트롤러가 여러 제출 큐의 명령을 임의 순서로 실행할 수 있어서 호스트가 순차로 제출해도 재정렬되어 순차 쓰기 제약을 위반할 수 있다는 점이다. 또 ZNS는 ZBC/ZAC와 달리 활성 존 개수에 상한을 둔다.
자주 묻는 질문
SSD가 꽉 찰수록 느려진다는 말이 사실인가요? 위 표가 그 근거다. 빈 공간이 줄어들면 GC가 고를 수 있는 블록 중에 무효 페이지 비율이 높은 것이 사라진다. 즉 유효 비율 u가 올라가고, 호스트 쓰기 1회당 실제 NAND 쓰기가 급격히 늘어난다. u가 0.5일 때 2배였던 것이 0.9면 10배가 된다. 실무적으로는 여유 공간을 남기는 것, 그리고 쓰지 않는 영역을 장치에 알려주는 것(TRIM 계열 명령)이 도움이 된다. 후자는 해당 페이지를 무효로 표시하게 해서 u를 낮추는 효과가 있다.
순차 쓰기와 랜덤 쓰기의 성능 차이가 SSD에서도 큰 이유는 뭔가요? HDD와 이유가 다르다. HDD는 헤드 이동 때문이지만, SSD는 GC 부담 때문이다. 순차 쓰기는 블록을 처음부터 끝까지 채우므로 나중에 그 영역이 갱신될 때 블록 전체가 한꺼번에 무효가 되고, 복사할 유효 페이지가 거의 없다. 반면 랜덤 쓰기는 여러 블록에 무효 페이지를 조금씩 흩뿌려서 어느 블록을 골라도 유효 페이지가 많이 남는다. 같은 용량을 써도 뒤에 청구되는 비용이 다르다.
매핑 테이블은 어디에 저장되나요? 전원이 꺼지면 사라지지 않나요? 접근 속도 때문에 동작 중에는 장치 내부의 휘발성 메모리에 올려두고 쓴다. 그래서 전원이 꺼져도 복구할 수 있도록 별도 장치가 필요하다. 일반적으로는 매핑 정보를 주기적으로 NAND에 내려쓰고, 각 페이지의 스페어 영역에 그 페이지가 어떤 논리 주소의 데이터인지를 함께 기록해두는 방식이 쓰인다. 앞서 인용한 FAU 자료도 페이지마다 32~64바이트 수준의 스페어 영역이 있다고 정리한다. 다만 구체적인 복구 절차와 전원 손실 대비 설계는 제품마다 다르므로, 정확한 동작은 대상 장치의 규격을 확인해야 한다.
매핑을 페이지 단위로 하지 않고 블록 단위로 하면 안 되나요? 가능하고 실제로 존재하는 방식이다. 블록 단위 매핑은 항목 수가 훨씬 적어서 테이블을 담을 메모리가 크게 줄어든다. 대신 논리 블록 안의 페이지 하나만 갱신되어도 처리해야 할 단위가 커져서 쓰기 증폭이 불리해진다. 그래서 두 방식을 섞은 하이브리드 구조도 쓰인다. 정리하면 매핑 세밀도는 DRAM 용량과 쓰기 증폭 사이의 교환이며, ZNS가 장치의 DRAM 필요량을 줄인다고 말하는 것도 이 축에서 이해할 수 있다.
GC는 항상 성능을 떨어뜨리나요? 장치가 한가할 때 미리 수행하면 호스트가 체감하는 지연은 크게 줄어든다. 앞서 본 것처럼 빈 블록을 미리 확보해두는 설계 자체가 지우기를 임계 경로에서 빼내려는 것이다. 문제가 되는 상황은 쓰기가 쉬지 않고 이어져서 미리 확보해둔 여유가 소진되고, 호스트 요청과 GC가 같은 자원을 놓고 경합할 때다. 지속 쓰기 성능이 초기 성능보다 낮게 측정되는 이유가 여기에 있다. 그래서 성능을 평가할 때는 짧은 버스트가 아니라 정상 상태(steady state)에 도달한 뒤의 값을 봐야 한다.