Chapter 8

고급 주제

  • 8.1 플래시 메모리
  • 8.2 데이터 무결성
  • 8.3 분산 시스템
  • 8.4 NFS
  • 8.5 AFS

마지막 장이야. 플래시 SSD, 데이터 무결성, 그리고 네트워크를 넘어서는 분산 파일 시스템까지 — 현대 스토리지의 고급 주제들을 다뤄볼게.

플래시 기반 SSD는 HDD와 물리적 특성이 완전히 달라. 기계적 부품이 없어서 탐색 시간이 없다는 장점이 있지만, 쓰기와 지우기의 비대칭성이라는 고유한 문제가 있지. 플래시 칩은 블록(128256개 페이지)과 페이지(보통 4KB)로 구성되는데, 읽기는 페이지 단위로 빠르고(수십 마이크로초), 쓰기도 페이지 단위지만 빈 페이지에만 쓸 수 있어 — 덮어쓰기가 불가능해. 지우기는 블록 단위로 매우 느려(수 밀리초). 페이지를 수정하려면 블록 전체를 지우고 다시 써야 하는데, 이걸 직접 하면 엄청 비효율적이야. 게다가 각 블록은 지우기 횟수에 제한(수천수만 회)이 있는 마모(wear) 문제도 있고.

이 문제들을 해결하는 게 SSD 내부의 **FTL(Flash Translation Layer)**이야. 호스트에게는 일반 블록 디바이스처럼 보이게 하면서, 논리 주소를 물리 주소로 매핑하고, 가비지 컬렉션을 하고, 마모를 평준화해. 현대 FTL은 대부분 **로그 구조(log-structured)**야 — LFS랑 같은 원리가 SSD 내부에서 돌아가는 거지! 쓰기 요청이 오면 현재 블록의 다음 빈 페이지에 순차적으로 쓰고, 같은 논리 주소에 다시 쓰면 새 물리 위치에 쓰고 이전 위치를 무효화해. 매핑 테이블이 논리 → 물리 매핑을 유지하는데, 페이지 레벨 매핑은 유연하지만 테이블이 크고(1TB SSD면 GB 단위), 블록 레벨은 테이블이 작지만 유연성이 부족해서, 하이브리드 매핑이 실전에서 많이 쓰여. **마모 평준화(wear leveling)**는 모든 블록이 비슷한 횟수로 지워지도록 관리하는 건데, 콜드 데이터가 특정 블록에 고정되면 그 블록만 안 지워지고 다른 블록이 먼저 죽으니까, FTL이 주기적으로 콜드 데이터를 옮겨. 파일이 삭제되면 OS가 TRIM 명령으로 SSD에게 알려줘서 가비지 컬렉션에 활용하게 해.

데이터 무결성은 저장한 데이터가 시간이 지나면서 조용히 손상될 수 있다는 무서운 이야기야. **잠복 섹터 에러(LSE)**는 특정 섹터가 안 읽히는 거라 적어도 문제를 알지만, **블록 손상(block corruption)**은 데이터가 조용히 바뀌는 **침묵의 실패(silent failure)**야. 디스크가 에러를 보고하지 않으니까 읽으면 데이터가 나오는데 그게 원래 쓴 데이터가 아닌 거지. 펌웨어 버그, 잘못된 위치 쓰기, 비트 플립 등이 원인이야. 핵심 대응 도구가 체크섬이야. 데이터를 쓸 때 체크섬을 함께 저장하고, 읽을 때 다시 계산해서 비교해. **CRC(Cyclic Redundancy Check)**가 가장 일반적이고, XOR은 단순하지만 특정 패턴을 못 잡고, 암호학적 해시(SHA-256)는 가장 강력하지만 비싸. 체크섬만으로는 잘못된 위치 쓰기(엉뚱한 블록에 쓰는 것)를 못 잡을 수 있어서, 체크섬에 물리적 위치 정보를 포함시키기도 해. **중간에 끊긴 쓰기(lost write)**는 디스크가 "썼어요"라고 하는데 실제로 안 쓴 경우인데, ZFS처럼 체크섬을 데이터와 다른 위치에 저장하는 방식으로 대응하지. 데이터가 오래 앉아 있으면 비트 로트로 조용히 손상될 수 있으니까, **스크러빙(scrubbing)**으로 주기적으로 모든 블록을 읽어서 체크섬을 확인하고 RAID 등으로 복구해. ZFS가 이 분야에서 가장 적극적인 파일 시스템인데, **머클 트리(Merkle tree)**로 메타데이터 체인 전체를 검증해.

이제 하나의 컴퓨터를 넘어서 분산 시스템으로 가보자. 여러 컴퓨터가 네트워크로 연결된 환경은 단일 시스템과 근본적으로 달라. 네트워크 실패(메시지 손실, 지연, 순서 뒤바뀜), 부분 실패(10대 중 3대만 죽는 상황), 예측 불가능한 성능 변동이 있지. 피터 도이치의 분산 컴퓨팅의 8가지 오류 — "네트워크는 신뢰할 수 있다", "지연 시간은 0이다" 같은 가정들이 전부 틀렸다는 거야. 신뢰할 수 있는 통신은 확인응답(ack) + 타임아웃 + 시퀀스 번호로 구축해 — TCP가 바로 이 원리지. 분산 시스템의 대표적 통신 추상화인 **RPC(Remote Procedure Call)**는 원격 서버의 함수를 로컬 호출처럼 쓰게 해주는데, 완전히 투명하게 만들 수는 없어. 서버에 도달 못 했는지 응답이 손실된 건지 구별할 수 없고, 멱등성이 없는 연산(출금 같은)은 재전송하면 두 번 실행될 수 있고, 네트워크 왕복 시간이 로컬 함수 호출과는 비교도 안 될 만큼 느리거든.

**NFS(Network File System)**는 Sun이 만든 분산 파일 시스템의 클래식이야. 클라이언트가 서버의 파일 시스템을 마운트하면 로컬 파일처럼 쓸 수 있지. NFSv2의 핵심은 무상태(stateless) 설계 — 서버가 클라이언트 상태를 기억하지 않아. OPEN 연산이 프로토콜에 존재하지 않고, 매 요청에 파일 핸들(볼륨 ID + inode 번호 + generation 번호)과 오프셋을 포함시켜. 무상태의 최대 장점은 서버 크래시 복구가 간단하다는 거야 — 서버가 다시 살아나면 클라이언트가 요청을 재전송하면 끝. 복원할 상태가 없으니까. 성능을 위해 클라이언트가 데이터를 로컬 캐시에 저장하는데, 캐시 일관성은 close-to-open 방식이야 — 파일을 닫을 때 서버로 flush하고, 열 때 서버에서 최신 버전을 확인해. GETATTR RPC로 파일의 수정 시간을 확인해서 캐시를 갱신하는데, 이게 너무 빈번하면 서버에 부하가 걸려서 속성 캐시로 일정 시간 동안 생략하기도 해. 동시에 같은 파일을 여러 클라이언트가 쓰면 일관성이 깨질 수 있는 게 한계야.

**AFS(Andrew File System)**는 NFS와 같은 시대에 CMU에서 만들어졌지만, **확장성(scalability)**에 집중했어. 핵심 관찰은 NFS에서 서버 접근이 너무 많다는 거였지. AFS의 해법은 두 가지야. 첫째, 전체 파일 캐싱 — 파일을 열 때 전체를 클라이언트의 로컬 디스크로 가져와서, 이후 모든 read/write가 로컬에서 수행돼. 서버 접촉이 open/close 때만 발생하지. 둘째, 콜백(callback) — 서버가 파일을 보낼 때 "이 파일이 바뀌면 알려줄게"라는 약속도 함께 보내. 다른 클라이언트가 수정하면 서버가 콜백 무효화를 보내서 캐시를 비우게 해. NFS의 polling(클라이언트가 물어봄) 대신 이벤트 기반(서버가 알려줌)이라 서버 부하가 훨씬 적어. 대신 서버가 **상태를 유지(stateful)**해야 하니까 크래시 복구가 복잡해지지 — 콜백 상태가 사라지면 모든 클라이언트 캐시를 무효화하거나 재검증해야 해. 일관성은 마지막 작성자 승리(last writer wins) — 마지막으로 close()한 내용이 반영돼.

NFS(무상태/단순/크래시 복구 쉬움) vs AFS(상태 유지/확장적/크래시 복구 복잡)는 분산 파일 시스템 설계의 근본적인 트레이드오프를 보여줘. AFS는 NFS만큼 널리 쓰이진 못했지만, "서버 부하를 줄이는 게 확장성의 핵심"이라는 교훈과 콜백 메커니즘은 현대 클라우드 스토리지의 이벤트 알림, 구독-발행 패턴에까지 영향을 미쳤어.


정리

8장 읽고 기억할 거 세 가지:

  1. SSD의 FTL은 로그 구조로 LFS와 같은 원리로 동작해. 플래시의 덮어쓰기 불가 + 블록 단위 지우기 비대칭성을 매핑 테이블과 가비지 컬렉션으로 해결하고, 마모 평준화로 SSD 수명을 관리해
  2. 침묵의 데이터 손상은 체크섬(CRC)으로 감지하고 RAID 등의 중복으로 복구해. 감지와 복구는 별개의 메커니즘이고, 주기적 스크러빙으로 잠재적 손상을 조기에 발견하는 게 중요해
  3. NFS(무상태/polling)와 AFS(상태 유지/콜백)는 분산 파일 시스템의 근본적 트레이드오프야. NFS는 크래시 복구가 간단하고, AFS는 서버 부하를 줄여 확장성이 좋아. 워크로드에 맞는 선택이 중요해