변경을 먼저 로그(저널)에 적고 그다음 실제 위치에 반영하는 기법.
무엇을 푸는가
파일 하나를 만드는 데도 여러 곳을 고쳐야 한다
- inode 할당
- 데이터 블록 할당
- 디렉터리 엔트리 추가
- 비트맵 갱신
중간에 전원이 나가면 어중간한 상태가 남는다
- 예) 블록은 할당됐는데 어느 파일도 안 가리킨다 (누수)
- 디렉터리에 이름은 있는데 inode 가 없다 (깨진 링크)
저널이 해결하는 방식
-
① 저널에 "이런 변경을 할 것이다" 를 먼저 기록하고 커밋
-
② 실제 위치에 반영
-
③ 저널에서 해당 항목 제거
-
크래시 후 재부팅하면
- 저널에 커밋된 항목 → 다시 실행(redo)
- 커밋 안 된 항목 → 버린다
-
전체 디스크 검사(fsck) 없이 빠르게 복구된다
무엇을 저널링하나
-
메타데이터만 — 기본값. 파일 구조는 지켜지지만 내용은 깨질 수 있다
- (ext4 의 ordered 모드)
-
데이터까지 — 안전하지만 모든 것을 두 번 쓴다 → 성능 저하
- (ext4 의 journal 모드)
-
대부분 메타데이터만 저널링한다 — 구조 일관성이 우선이다
페이지 캐시와의 관계
- 쓰기는 보통 페이지 캐시에만 반영되고 나중에 디스크로 내려간다(write-back)
- 빠르지만 캐시에만 있는 데이터는 크래시 시 사라진다
fsync() 를 불러야 디스크까지 내려간 것이 보장된다 DB 가 커밋마다 fsync 를 부르는 이유이고, 그것이 느린 이유이기도 하다
순서 보장이 없으면 저널도 무용지물
"저널에 먼저 쓰고 그다음 본 위치에 쓴다" 가 성립하려면 디스크가 그 순서대로 실제 기록해야 한다
- 그런데 디스크는 내부 캐시를 갖고 순서를 바꿔 쓴다
- 저널 커밋 시점에 캐시 플러시(FLUSH CACHE) 명령이 필요하다
- 이것이 fsync 가 느린 진짜 이유다
배터리 백업 캐시가 있는 RAID 컨트롤러는 플러시를 생략할 수 있어 DB 성능이 크게 오른다
다른 접근 — 로그 구조와 CoW
- 저널링 — 변경을 두 번 쓴다 (저널 + 본 위치)
- CoW — 새 위치에 쓰고 포인터를 원자적으로 바꾼다
- 제자리 갱신이 없으므로 어중간한 상태가 원천적으로 없다
- ZFS · Btrfs 가 이 방식. 스냅샷이 거의 공짜다
- 로그 구조 모든 쓰기를 순차 append 로 만든다 (LFS)
- SSD 내부 FTL 과 LSM 트리가 같은 발상이다
DB 의 WAL 과 같은 원리
파일 시스템의 저널과 DB 의 WAL(Write-Ahead Log)은 원리가 동일하다 — "바꾸기 전에 무엇을 바꿀지 먼저 안전하게 적는다". 계층이 겹치면 이중 기록이 되므로, DB 는 보통 파일 시스템 저널링을 메타데이터로 최소화하고 자기 WAL 에 의존한다.
면접 함정
- ❌ "저널링이 데이터를 보호한다" → 기본은 메타데이터만이다.
- ❌ "write()가 성공하면 디스크에 있다" → 페이지 캐시일 뿐이다.
fsync가 필요하다.