LabHub
배우기 러닝패스 코스

Computer Architecture

Storage Devices — From Spinning Platters to Parallel Queues

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

HDD 는 물리적으로 헤드를 움직여야 해서 랜덤 접근이 밀리초 단위이고, SSD 는 그 제약이 없는 대신 지우기 단위가 커서 쓰기에 고유한 비용 구조를 갖는다.

Flow map: HDD. · 순차와 랜덤의 격차가 백 배 이상 · SSD. · 호스트가 요청한 양보다 실제로 더 많이 쓰게 되는데

왜 이게 필요했나

데이터베이스와 파일시스템 설계의 상당 부분은 "회전하는 원판"이라는 전제 위에 세워졌다. 순차 접근이 랜덤 접근보다 백 배 빠르다는 사실이 B 트리의 구조, 로그 선행 기록, 로그 구조 병합 트리 같은 아이디어를 낳았다. 저장 매체가 바뀌면 그 전제 중 무엇이 남고 무엇이 무너지는지 알아야 한다.

어떻게 동작하나

HDD. 한 블록을 읽으려면 헤드를 해당 트랙으로 옮기고(탐색 시간, 보통 5~10ms) 원하는 섹터가 헤드 밑으로 돌아오기를 기다린다(회전 지연, 7200rpm 이면 평균 약 4ms). 그래서 랜덤 읽기는 초당 수백 회가 한계다. 반면 헤드를 옮기지 않고 이어서 읽으면 초당 수백 MB 가 나온다. 순차와 랜덤의 격차가 백 배 이상인 이 특성이 모든 고전적 설계의 출발점이다.

SSD. 움직이는 부품이 없어 랜덤 읽기가 마이크로초 단위다. 하지만 쓰기가 대칭이 아니다. 낸드 플래시는 페이지(수 KB) 단위로 쓰고 블록(수 MB) 단위로만 지울 수 있다. 이미 쓴 자리에 덮어쓸 수 없으므로 컨트롤러는 새 자리에 쓰고 옛 자리를 무효로 표시한 뒤, 나중에 유효 페이지를 모아 옮기고 블록을 통째로 지운다(가비지 컬렉션). 이 과정에서 호스트가 요청한 양보다 실제로 더 많이 쓰게 되는데, 그 비율을 쓰기 증폭(write amplification)이라 한다. 또 각 셀은 지울 수 있는 횟수에 한계가 있어 컨트롤러가 마모 평준화(wear leveling)로 골고루 쓴다.

NVMe. SATA 는 명령 큐가 하나에 깊이 32였다. NVMe 는 큐를 수만 개까지, 각 큐를 수만 깊이로 둘 수 있다. 여기서 중요한 전환이 일어난다. 낱개 요청의 지연은 크게 줄지 않지만, 동시에 던지는 요청 수(큐 깊이)를 늘려야 대역폭이 채워진다. 큐 깊이 1로 측정한 IOPS 는 장치 성능의 일부만 보여 준다.

현장에서 만나는 모습

PostgreSQL 의 random_page_cost 기본값 4.0 은 회전 디스크 기준이다. SSD 나 NVMe 위에서 이 값을 그대로 두면 옵티마이저가 랜덤 접근을 실제보다 네 배 비싸게 평가해 인덱스를 과소평가한다. "인덱스를 만들었는데 안 탄다"는 문의의 상당수가 이 설정 하나를 1.1 근처로 낮추면 해결된다. 매체가 바뀌었으면 매체를 전제로 한 비용 모델도 바꿔야 한다는 사례다.

반대로 여전히 유효한 전제도 있다. 순차 쓰기가 랜덤 쓰기보다 유리하다는 점은 SSD 에서도 그대로다. 이유가 달라졌을 뿐이다. HDD 에서는 헤드를 안 움직여서였고, SSD 에서는 가비지 컬렉션과 쓰기 증폭을 줄여 주기 때문이다.

숫자로 감을 잡기

저장장치의 차이는 표로 보면 분명합니다. 자릿수가 다릅니다.

HDD SATA SSD NVMe SSD 메모리
무작위 읽기 지연 5~10 ms 0.1 ms 0.02 ms 0.0001 ms
IOPS(무작위 4K) 100~200 수만 수십만~백만
순차 대역폭 150 MB/s 500 MB/s 3~7 GB/s 50 GB/s+
큐 깊이 1(사실상) 32 65,536 × 여러 큐

HDD 의 무작위 IOPS 가 200 밖에 안 되는 이유 는 물리적입니다. 헤드가 움직이고 원판이 돌아야 하므로 한 번에 5~10ms 가 듭니다. 그래서 데이터베이스를 HDD 에 두면 초당 200 건의 무작위 조회가 상한입니다.

NVMe 가 SATA SSD 보다 빠른 것은 매체가 아니라 인터페이스 때문입니다. SATA 는 큐가 하나에 깊이 32 인데, NVMe 는 큐를 여러 개 두고 각각 깊이가 65,536 입니다. CPU 코어마다 자기 큐를 가질 수 있어 병렬성이 완전히 다릅니다.

순차와 무작위를 구분하는 것이 설계다

같은 장치에서도 접근 패턴에 따라 성능이 열 배 이상 갈립니다.

세 번째가 실무에서 자주 걸립니다. 이미지 썸네일이나 로그 조각을 파일 하나씩 두면 디스크 용량은 남는데 inode 가 떨어져 더 이상 쓸 수 없습니다. df -i 로 확인합니다.

쓰기가 실제로 디스크에 닿는 시점

애플리케이션이 write() 를 불렀다고 데이터가 디스크에 있는 것이 아닙니다.

앱 버퍼 → 페이지 캐시(커널) → 장치 캐시 → 매체
              ↑ write() 는 여기까지만 보장한다
              ↑ fsync() 가 아래로 밀어낸다

전원이 나가면 fsync 하지 않은 것은 사라집니다. 데이터베이스가 커밋마다 fsync 하는 이유이고, 그것이 쓰기 성능의 상한을 정합니다.

여기서 쓰기 캐시가 있는 디스크의 함정 이 나옵니다. 장치가 캐시에만 넣고 "완료" 를 돌려주면 fsync 도 거짓말이 됩니다. 서버용 SSD 는 전원 손실 보호 (PLP) 커패시터를 달아 이 문제를 막습니다. 소비자용 SSD 를 DB 서버에 쓰면 안 되는 이유가 이것입니다.

이어지는 퀴즈에서 확인할 것

매체가 바뀌면 어떤 설계 전제가 무너지고 어떤 것이 남는지를 구분할 수 있는지 확인한다.