RDB, AOF, and What You Can Afford to Lose
한국어 원문으로 표시합니다.
한 줄 요약
영속성 설정은 "몇 초어치를 잃어도 되는가"라는 질문에 대한 답이다. 답이 "한 건도 안 된다"면 Redis 가 맞는 도구인지부터 다시 봐야 한다.
왜 이게 필요했나
Redis 는 인메모리 저장소입니다. 프로세스가 죽으면 메모리는 사라집니다. 그래서 두 가지 영속성 방식을 제공합니다.
RDB 는 주기적 스냅샷입니다. 설정 예를 보면 save 3600 1, save 300 100, save 60 10000 — 한 시간 안에 1건 이상 변경, 5분 안에 100건, 1분 안에 10000건 변경이면 스냅샷을 뜹니다. 장점은 파일 하나라 백업과 복구가 간단하고 재시작이 빠르다는 것입니다. 단점은 마지막 스냅샷 이후의 쓰기를 전부 잃는다는 것입니다.
AOF 는 모든 쓰기 명령을 로그로 남깁니다. appendfsync everysec 이 실무 기본값이고, 최악의 경우 1초어치를 잃습니다. always 로 두면 유실이 거의 없지만 쓰기마다 fsync 라 처리량이 크게 떨어집니다. no 는 OS 에 맡기는 것이라 유실 구간이 커집니다.
어떻게 동작하나
실무의 표준은 둘을 함께 쓰는 혼합 모드입니다. AOF 를 켜고 aof-use-rdb-preamble yes 로 두면, AOF 리라이트 시 앞부분을 RDB 형식으로 저장하고 그 뒤에 증분 명령을 붙입니다. 복구가 빠르면서 유실 구간도 1초로 유지됩니다.
Redis 7 의 Multi-Part AOF 는 이것을 한 단계 더 정리해서, 전용 디렉터리에 base 파일과 incr 파일을 나눠 둡니다. 리라이트 중 디스크 공간이 두 배로 튀는 문제가 줄었습니다.
여기서 정직하게 짚을 부분이 있습니다. 영속성을 켜도 Redis 는 데이터베이스가 아닙니다. 복제는 비동기라서 마스터가 죽는 순간 복제본에 도달하지 못한 쓰기가 있을 수 있습니다. WAIT 명령으로 일부 개선할 수 있지만 완전한 동기 복제는 아닙니다. 그래서 "절대 잃으면 안 되는" 데이터의 1차 저장소로는 적절하지 않습니다.
현장에서 만나는 모습
fsync 비용을 과소평가하는 경우가 많습니다. appendfsync always 로 바꾸고 처리량이 절반 이하로 떨어졌다는 보고가 흔합니다. 디스크가 느린 환경에서는 AOF 쓰기가 밀려 메인 스레드가 블록되기도 합니다.
RDB 스냅샷도 공짜가 아닙니다. fork 로 자식 프로세스를 만들어 스냅샷을 뜨는데, copy-on-write 때문에 쓰기가 많으면 순간적으로 메모리가 크게 늘어납니다. 4GB 인스턴스가 스냅샷 중에 6GB 를 쓰는 일이 생깁니다. maxmemory 를 물리 메모리에 딱 맞춰 잡으면 이때 OOM 이 납니다. 보통 물리 메모리의 절반 정도를 maxmemory 로 잡습니다.
두 방식을 나란히 놓으면
| RDB (스냅숏) | AOF (명령 로그) | |
|---|---|---|
| 저장하는 것 | 그 시점의 전체 데이터 | 바꾼 명령을 순서대로 |
| 파일 크기 | 작다(압축된 이진) | 크다(재작성으로 줄인다) |
| 재시작 속도 | 빠르다 | 느리다(명령을 다시 실행) |
| 최악의 유실 | 마지막 스냅숏 이후 전부 | appendfsync 에 달림 |
| 부하 | fork 순간 메모리가 튄다 | 계속 쓰지만 완만하다 |
appendfsync 가 AOF 의 핵심 손잡이입니다.
always 쓸 때마다 fsync — 거의 안 잃지만 아주 느리다
everysec 1초마다 fsync — 최악 1초 유실. 사실상 표준
no OS 에 맡김 — 빠르지만 몇 초를 잃을 수 있다
둘을 함께 켜는 것이 기본형 입니다. AOF 로 최근 1초까지 지키고, RDB 로 빠른 복구와 백업 파일을 얻습니다. Redis 는 재시작할 때 AOF 가 있으면 그것을 우선 씁니다.
fork 가 만드는 메모리 급증
RDB 스냅숏과 AOF 재작성은 자식 프로세스를 fork 해서 합니다. 리눅스의 copy-on-write 덕분에 처음에는 메모리를 공유하지만, 저장하는 동안 부모가 바꾸는 페이지마다 복사본이 생깁니다.
쓰기가 많은 순간에 스냅숏이 겹치면 메모리가 최대 두 배 까지 오릅니다. 컨테이너 한도에 걸려 OOM 으로 죽는 사고가 여기서 납니다.
maxmemory 4gb ← 데이터 상한
컨테이너 한도 6~8gb ← fork 여유를 남긴다
vm.overcommit_memory = 1 을 권하는 이유도 이것입니다. 이 값이 0이면 커널이
"메모리가 모자랄 것 같다" 며 fork 를 거부해 저장 자체가 실패 합니다. Redis 는
로그에 경고를 남기지만 조용히 지나치기 쉽습니다.
무엇을 잃어도 되는지 먼저 정한다
- 순수 캐시 — 지속성이 필요 없습니다. 둘 다 끄면 fork 부담도 사라집니다. 다시 채우면 그만입니다.
- 세션 저장소 — 잃으면 전원 로그아웃입니다. AOF everysec 정도.
- 큐·작업 상태 — 잃으면 작업이 사라집니다. AOF + 복제본.
- 원본 데이터 — Redis 를 원본으로 쓰지 않는 편이 낫습니다. 굳이 쓴다면 AOF always 와 복제, 그리고 정기 백업까지.
가장 흔한 실수는 캐시인데 지속성을 켜 두는 것 입니다. 필요 없는 fork 비용과 디스크 I/O 를 내면서, 정작 복구할 때는 옛 캐시가 되살아나 문제를 만듭니다.
다음 확인에서 볼 것
영속성 설정 변경은 실습 파드의 다른 실습에 영향을 줄 수 있어 퀴즈로 대신합니다. RDB·AOF 의 유실 범위와 복구 비용을 상황별로 구분할 수 있는지 확인하고 코스를 마칩니다.