운영체제 아주 쉬운 세가지 이야기 16주차(데이터 무결성과 보호)
2025. 12. 23. 18:56ㆍ개념 공부
파일 시스템 데이터 무결성(Data Integrity) 한 번에 정리
디스크는 “통째로만” 망가지지 않는다. “조금씩, 조용히 틀어지는 것들”을 감지·복구하는 구조를 알면 RAID, 파일시스템, DB 로그(WAL) 설계가 한 번에 연결된다.
1. 우리가 지키고 싶은 것: Data Integrity
운영체제/파일시스템 입장에서 데이터 무결성의 목표는 단순하다:
- “내가 저장한 데이터”가
- 나중에 다시 읽었을 때 똑같이 돌아오는 것
그런데 실제 디스크는 생각보다 온갖 방식으로 망가진다.
1-1. 옛날 모델: Fail-stop (디스크 전체가 죽는다)
- 옛날 RAID 가정: 디스크는 정상이거나 완전히 죽음 둘 중 하나.
- 이러면 설계가 쉽다. 죽은 디스크만 패리티/미러로 복구하면 끝.
1-2. 요즘 모델: Fail-partial (일부 블록만 이상해진다)
현실의 디스크는 더 짓궂다.
- LSE (Latent Sector Error):
- 해당 섹터가 물리적으로 손상되어 읽을 수 없음 (에러를 되돌려줌).
- 헤드가 표면을 긁었거나, 코스믹 레이로 비트 플립 등의 이유.
- 디스크 내부 ECC가 “이건 진짜 못 읽겠다”고 에러를 알려줌 → 비-침묵(non-silent) 오류.
- Block corruption (silent corruption):
- 펌웨어 버그로 잘못된 위치에 블록을 씀.
- 호스트 ↔ 디스크 버스 전송 중 데이터가 깨졌는데, 그 상태로 디스크에 저장.
- ECC는 “비트는 잘 저장됐네?”라고 말하지만, 내용은 사용자가 원했던 것과 다름.
- 이 경우 디스크는 에러를 안 내고 “정상 데이터”라고 줌 → 침묵(silent) 오류.
요즘 스토리지 설계에서 “디스크가 한 번에 죽는다”만 가정하면 이미 설계가 틀린 것이다. 현업에서는 “부분적으로 망가진 디스크 + 여전히 에러를 안 내는 경우”를 기본 전제로 두고 방어해야 한다.
2. LSE(Latent Sector Error)는 어떻게 처리할까?
LSE의 특징: “이 블록은 못 읽겠어요”라고 디스크가 솔직하게 말해준다.
- RAID-1(미러): 다른 디스크의 복제본에서 해당 블록을 읽어서 복구.
- RAID-4/5(패리티): 같은 스트라이프의 나머지 블록 + 패리티로 해당 블록 재구성.
즉, LSE 자체는 “복구가 쉬운 오류”다.
문제는 “언제 발견하냐” 이다. 그래서 뒤에서 나오는 scrubbing이 중요해진다.
RAID 재빌드 + LSE가 겹치면?
디스크 하나가 죽어서 재구성(rebuild) 중인데, 살아 있는 다른 디스크들 중 하나에서 LSE가 터지면 → 재구성이 멈출 수 있다.
이를 막으려고 NetApp RAID-DP처럼 이중 패리티(두 개의 패리티 블록)를 쓰는 구조가 등장했다.
디스크 하나가 죽어서 재구성(rebuild) 중인데, 살아 있는 다른 디스크들 중 하나에서 LSE가 터지면 → 재구성이 멈출 수 있다.
이를 막으려고 NetApp RAID-DP처럼 이중 패리티(두 개의 패리티 블록)를 쓰는 구조가 등장했다.
3. 더 무서운 건 Silent Corruption → Checksum이 필요하다
LSE처럼 “읽을 수 없습니다”라고 말해주면 쉬운데, “틀린 데이터를 아무 말 없이 주는 경우”가 더 무섭다.
이걸 잡는 대표적인 도구가 Checksum(체크섬)이다.
3-1. 체크섬이란?
- 어떤 데이터 블록(예: 4KB)에 대해 함수 f(data)를 계산해서 작은 요약 값(예: 4~8바이트)을 만든 것.
- 저장할 때:
데이터 + 체크섬을 같이 저장. - 읽을 때:
- 데이터를 읽고,
- 같이 저장된 체크섬도 읽은 뒤,
- 다시 한 번 현재 데이터로 체크섬을 계산해서 둘을 비교.
- 다르면 = 중간에 데이터가 바뀐 것 → corruption 감지 성공.
중요 포인트
체크섬은 “데이터가 틀렸다”를 감지하는 용도일 뿐이다. 틀렸다는 걸 알아도, 다른 복제본이 없으면 그냥 에러를 내고 끝이다. 그래서 “체크섬 + 복제(미러, 패리티, 백업)”을 항상 같이 생각해야 한다.
체크섬은 “데이터가 틀렸다”를 감지하는 용도일 뿐이다. 틀렸다는 걸 알아도, 다른 복제본이 없으면 그냥 에러를 내고 끝이다. 그래서 “체크섬 + 복제(미러, 패리티, 백업)”을 항상 같이 생각해야 한다.
3-2. 대표적인 체크섬 함수들
- XOR 기반 체크섬
- 블록을 여러 조각으로 나누고, 각 조각을 XOR 해서 하나의 값으로 만든다.
- 간단하고 빠르지만, 특정 패턴에서는 오류를 못 잡을 수 있다 (각 조각의 같은 비트 위치가 둘 다 바뀌면 XOR 결과가 그대로).
- 덧셈 기반(additive) 체크섬
- 각 바이트/워드를 2의 보수 덧셈으로 더하고 overflow는 무시.
- 역시 빠르지만, 데이터 순서 변경/shift에는 취약.
- Fletcher 체크섬
- 두 개의 합
s1과s2를 누적하는 방식. - 단순 덧셈보다 강력하고, 구현도 가벼운 편.
- 단일 비트, 이중 비트, 많은 burst 오류를 잘 잡는다.
- 두 개의 합
- CRC (Cyclic Redundancy Check)
- 데이터 전체를 하나의 큰 이진수로 보고, 특정 다항식로 modulo 연산한 나머지를 사용.
- 네트워크, 스토리지에서 널리 쓰이는 강력한 체크섬.
실무에서는 “무조건 가장 강력한 체크섬”을 쓰지 않는다. 오류 검출 능력 vs CPU 비용 vs I/O 패턴을 같이 보고 선택한다.
예를 들어, 고성능 스토리지에서는 NIC/스토리지 칩에 CRC 오프로드를 걸어서 CPU 부담을 줄이기도 한다.
예를 들어, 고성능 스토리지에서는 NIC/스토리지 칩에 CRC 오프로드를 걸어서 CPU 부담을 줄이기도 한다.
4. 체크섬은 디스크 어디에 저장할까? (레이아웃)
4-1. 이상적인 형태: 블록마다 붙이기
각 데이터 블록 D마다 C(D)를 바로 옆에 붙여 저장:
[ C(D0) | D0 ] [ C(D1) | D1 ] ...
- 디스크가 520바이트 섹터(512 + 8바이트 메타데이터)를 지원하면 이런 식으로 구현 가능.
- 장점: 블록 하나를 수정할 때 쓰기 하나로 해결 가능 (체크섬+데이터 같이).
4-2. 현실적인 형태: 체크섬들을 모아서 저장
대부분의 일반 디스크는 512/4096 바이트 섹터만 지원.
- 여러 개의
C(Di)를 하나의 섹터에 모아서 저장:[ C(D0), C(D1), C(D2), C(D3), C(D4) ] // 체크섬 섹터 [ D0, D1, D2, D3, D4 ] // 데이터 섹터들 - 단점: D1 하나를 덮어써도, 그에 해당하는 체크섬 섹터를 읽고 → 수정하고 → 다시 써야 함 (추가 I/O 발생).
“체크섬 위치”는 성능에 직접적인 영향을 준다. 읽기 경로에서 체크섬 때문에 랜덤 I/O가 늘어나지 않게 설계하는 게 중요하다. 고급 파일시스템(ZFS, btrfs 등)은 메타데이터 구조에 체크섬을 같이 넣어서 이 문제를 줄이려고 한다.
5. 체크섬 사용 흐름: 읽을 때 무엇을 하나?
- 블록 D와 저장된 체크섬
Cs(D)를 같이 읽는다. - 현재 읽어온 D에 대해 다시 체크섬
Cc(D)를 계산한다. - 비교:
Cs(D) == Cc(D)→ 아마도 정상, 그대로 사용자에게 반환.Cs(D) != Cc(D)→ corruption 감지.
- RAID/미러/백업 등 다른 복제본이 있으면 거기서 다시 읽어오거나 재구성.
복제본이 없으면? → 에러를 내고 끝이다. “체크섬 + 중복 저장” 구조가 중요하다는 걸 다시 확인.
6. 특수한 장애 1: Misdirected Write (엉뚱한 곳에 잘 쓰기)
Misdirected write는 “쓰기 자체는 성공했는데, 잘못된 위치에 쓴 경우”다.
- 단일 디스크:
- 원래는 블록 x에 써야 하는데, y에 써버림 → Dy가 덮어써짐.
- RAID:
- 원래는 디스크 i의 블록 x에 써야 하는데, 디스크 j의 다른 블록에 써버릴 수도 있다.
일반 체크섬만 쓰면 문제:
- 데이터 내용은 일관되게 저장되었기 때문에, 체크섬은 정상일 수 있다.
- 하지만 “여기가 그 데이터가 있어야 할 장소인지”는 알 수 없다.
해결: 체크섬에 “위치 정보(Physical ID)”를 같이 넣기
- 체크섬 정보에
disk 번호,block 번호를 같이 저장.
- 읽을 때:
- “나는 디스크 1의 블록 4를 읽고 있다”는 걸 알고,
- 함께 저장된 메타정보가
disk=1, block=4인지 확인. - 다르면 → “이 블록은 잘못된 위치에 쓰인 것” = misdirected write 감지.
스토리지/DB 설계에서 “데이터 내용”만 검증하면 충분하다고 생각하면 안 된다. “데이터 내용 + 이게 정말 그 데이터가 있어야 할 위치인지”를 같이 검증해야 컨트롤러/펌웨어 버그까지 방어할 수 있다.
7. 특수한 장애 2: Lost Write (쓴다고 했는데 사실 안 씀)
Lost write는 다음 상황이다:
- 디바이스/컨트롤러가 OS에 “쓰기 완료”라고 응답했는데,
- 실제로는 디스크에 새 데이터가 저장되지 않고 예전 데이터가 그대로 남아있는 경우.
문제: 기본 체크섬/위치 정보로는 못 잡는다.
- 예전 데이터 + 예전 체크섬이 그대로 남아있기 때문.
해결 전략 예시
- Write verify (read-after-write)
- 쓰기 직후에 바로 다시 읽어서 원하는 데이터가 맞는지 확인.
- 정확하지만 I/O 2배 → 매우 느려짐.
- 상위 계층에서의 end-to-end 체크섬 (예: ZFS)
- 각 데이터 블록에 대한 체크섬을 inode/간접 블록 등 상위 메타데이터에 같이 저장.
- 데이터 블록만 로스트됐다면, 그 블록을 읽을 때 상위 메타데이터의 체크섬과 비교해서 어긋남을 감지할 수 있다.
- 물론, 데이터와 메타데이터 둘 다 동시에 로스트되면 여전히 놓칠 수 있다 (확률은 매우 낮지만).
8. Scrubbing: “안 읽히는 데이터”도 정기검진
대부분의 데이터는 자주 읽히지 않는다. 그러면?
- 아무도 안 읽는 사이에 bit rot(조용한 부식)이 쌓일 수 있다.
- RAID/미러라도, 두 복제본이 모두 조금씩 망가지면 복구 불가능.
Disk Scrubbing
- 주기적으로(예: 밤/주말에) 디스크 전체를 한 바퀴 돌며:
- 모든 블록을 읽고,
- 체크섬을 검증한다.
- 문제가 발견되면:
- 다른 복제본에서 재복구.
- 맵/로그에 “이 블록은 나중에 쓰면 안 됨” 등으로 표시.
시니어 관점 TIP
실무에서 scrub은 “백업이 잘 돌아가고 있는지 검증”하는 것만큼 중요하다. 안 쓰는 데이터는 실제로 복구 필요 시점까지 아무도 건드리지 않기 때문에, 정기 scrub이 없으면 “복제본 둘 다 망가진 뒤에야” 문제를 알게 되는 경우가 생긴다.
실무에서 scrub은 “백업이 잘 돌아가고 있는지 검증”하는 것만큼 중요하다. 안 쓰는 데이터는 실제로 복구 필요 시점까지 아무도 건드리지 않기 때문에, 정기 scrub이 없으면 “복제본 둘 다 망가진 뒤에야” 문제를 알게 되는 경우가 생긴다.
9. 체크섬의 오버헤드: 공짜는 없다
9-1. 공간 오버헤드
- 디스크 공간:
- 예: 4KB 블록마다 8바이트 체크섬 → 약 0.19% 정도 추가 공간.
- 대부분의 시스템에서는 수용 가능한 수준.
- 메모리:
- 읽을 때 잠깐 체크섬도 같이 갖고 있어야 하지만, 곧 버리면 부담 작음.
- 메모리까지 end-to-end 보호(메모리 내 체크섬 유지)를 하려면 추가 메모리 소모.
9-2. 시간/CPU 오버헤드
- 저장할 때 한 번, 읽을 때 한 번 → 매번 체크섬 계산 필요.
- CPU 오버헤드를 줄이는 패턴:
- 데이터 복사와 체크섬 계산을 한 번의 루프로 합치기 (예: 커널 버퍼 → 유저 버퍼로 복사하면서 동시에 체크섬).
- 추가 I/O:
- 체크섬이 데이터와 물리적으로 분리되어 있으면, 체크섬 읽기/쓰기용 I/O가 추가.
- Scrubbing으로 인한 백그라운드 읽기 I/O.
“무결성을 극한까지 높이겠다”는 말은 곧 성능/비용 trade-off를 받아들이겠다는 뜻이다. 실제 서비스에서는 “어디까지 보호할 것인가(무결성 레벨)”와 “이걸 위해 얼마까지 낼 것인가(성능/비용)”를 명확히 정하고 설계해야 한다.
10. 정리
- 1) 디스크는 부분적으로도 고장난다
- LSE: 읽을 때 에러로 드러나는 문제 (복구는 쉬움).
- Corruption: 에러 없이 틀린 데이터를 주는 문제 (체크섬 필요).
- 2) 체크섬은 “감지용”이다. 복구하려면 반드시 복제본이 필요하다.
- 3) 체크섬 설계 요소
- 어떤 함수? (XOR / Add / Fletcher / CRC …)
- 어디에 저장? (블록 옆? 별도 영역?)
- 무엇까지 포함? (데이터만? 위치 정보까지?)
- 4) 특수 장애를 항상 상상해볼 것
- Misdirected write → 위치 정보로 탐지.
- Lost write → read-after-write, 상위 메타데이터의 end-to-end 체크섬.
- 5) Scrubbing으로 “잘 안 쓰는 데이터”까지 정기검진
- 6) 성능/공간 오버헤드를 항상 같이 본다
- 강한 보호 = 더 많은 CPU / I/O / 디스크 공간.
주니어 단계에서는 “체크섬이 있다/없다” 수준만 보게 되는데, 한 단계 위로 올라가면 “어떤 장애 모델을 가정하고, 어떤 레이어에 어떤 방식으로 체크섬과 복제를 두었는가”가 보인다.
이 챕터 내용은 나중에 DB의 WAL, 분산 스토리지, 오브젝트 스토리지(S3류), 로그 수집 시스템까지 전부 다시 응용된다. “데이터가 어떻게 틀어질 수 있는지를 구체적으로 상상해 보는 습관”이 가장 큰 자산이다.
이 챕터 내용은 나중에 DB의 WAL, 분산 스토리지, 오브젝트 스토리지(S3류), 로그 수집 시스템까지 전부 다시 응용된다. “데이터가 어떻게 틀어질 수 있는지를 구체적으로 상상해 보는 습관”이 가장 큰 자산이다.
'개념 공부' 카테고리의 다른 글
| 운영체제 아주 쉬운 세가지 이야기 17주차(프레시 기반의 SSD) (0) | 2025.12.29 |
|---|---|
| 운영체제 아주 쉬운 세가지 이야기 17주차(RAID) (0) | 2025.12.29 |
| 운영체제 아주 쉬운 세가지 이야기 15주차(크래시 일관성: FSCK와 저널링) (1) | 2025.12.16 |
| 운영체제 아주 쉬운 세가지 이야기 15주차(지역성과 Fast File System) (0) | 2025.12.16 |
| 운영체제 아주 쉬운 세가지 이야기 14주차(파일 시스템 구현) (0) | 2025.12.09 |