Redis는 인메모리 데이터베이스임에도 불구하고 데이터 영속성을 보장하기 위해 두 가지 메커니즘을 제공합니다. 바로 AOF(Append Only File) 로그와 RDB(Redis Database) 스냅샷입니다. 두 방식은 모두 디스크에 기록되지만 저장하는 정보의 형태가 다니다. AOF는 실행된 명령어 자체를 텍스트로 기록하는 반면, RDB는 메모리 상태를 바이너리 형태로 직렬화하여 저장합니다.
이번 글에서는 RDB 스냅샷의 동작 원리와 실제 활용 방식을 살펴보겠습니다.
스냅샷의 개념
스냅이란 특정 순간의 상태를 포착하는 것을 의미합니다. 사진으로 순간을 남기는 것과 유사하게, RDB 스냅샷은 실행 당시 메모리에 존재하는 전체 데이터를 디스크에 보관합니다. 이는 AOF와 대조적인데, AOF는 데이터 자체가 아닌 데이터를 변경하는 명령어의 흐름을 기록하기 때문입니다.
복구 시점에서 보면 RDB 방식이 더 효율적입니다. RDB 파일을 메모리로 직접 적재하기만 하면 되지만, AOF는 기록된 명령어를 순차적으로 재실행해야 하므로 상대적으로 느립니다.
스냅샷 생성 명령어
Redis는 RDB 파일 생성을 위해 두 가지 명령어를 제공합니다.
| 명령어 | 실행 방식 | 특징 |
|---|---|---|
SAVE | 메인 스레드 동기 실행 | 실행 중 모든 요청 처리 블로킹 |
BGSAVE | 별도 자식 프로세스 생성 | 메인 스레드 비차단, 백그라운드 실행 |
운영 환경에서는 SAVE 명령어를 직접 실행하는 경우가 드뭅니다. 대신 BGSAVE를 활용하거나 설정 파일을 통해 자동화합니다.
자동 스냅샷 설정
Redis 설정 파일(redis.conf)에는 다음과 같은 기본값이 포함되어 있습니다:
save 900 1
save 300 10
save 60 10000
이 설정은 OR 조건으로 동작합니다. 각각의 의미는 다음과 같습니다:
- 900초(15분) 내 데이터 변경이 1회 이상 발생
- 300초(5분) 내 데이터 변경이 10회 이상 발생
- 60초(1분) 내 데이터 변경이 10,000회 이상 발생
참고로 설정 항목 이름이 save이지만 실제로는 BGSAVE를 호출하여 자식 프로세스가 작업을 수행합니다.
전체 스냅샷의 특성
RDB는 전체 스냅샷(Full Snapshot) 방식을 채택합니다. 매번 실행될 때마다 메모리의 모든 키-값 쌍을 디스크에 기록하므로 상당한 I/O 부하가 발생합니다.
이로 인해 빈번한 스냅샷은 서버 성능에 악영향을 미치고, 반대로 간격이 넓으면 장애 발생 시 손실되는 데이터량이 증가합니다. 일반적으로 5분 이상의 간격을 설정하는 경우, 최악의 시나리오에서는 5분치 데이터가 유실될 수 있습니다.
AOF는 초기 단위로 명령어를 기록할 수 있어 상대적으로 데이터 손실 범위가 작지만, RDB는 파일 크기와 생성 비용 때문에 빈번한 실행이 어렵습니다.
BGSAVE 중 데이터 수정 가능 여부
BGSAVE 실행 중 핵심적인 질문이 있습니다: 메인 스레드가 여전히 쓰기 작업을 수행할 수 있는가? 답은 가능하다입니다.
이는 Copy-On-Write(COW) 메커니즘 덕분입니다. fork() 시스템 콜로 자식 프로세스를 생성하면 부모-자식은 동일한 물리 메모리 페이지를 공유합니다. 페이지 테이블만 복제되고 실제 데이터는 공유됩니다.
스냅샷 생성 과정을 단계별로 살펴보면:
fork()호출로 자식 프로세스 생성 (이 시점에서 메인 스레드는 짧게 블로킹됨)- 부모(메인 스레드)와 자식이 동일한 물리 메모리 공유
- 자식 프로세스는 공유 메모리를 읽어 RDB 파일 작성
- 메인 스레드가 읽기만 수행하면 물리 메모리 변경 없음
- 메인 스레드가 특정 키를 수정하면 해당 페이지만 복제되어 별도의 물리 메모리 할당
예를 들어 키 session:user:1001를 수정한다고 가정하면:
# fork 직후: 부모와 자식이 동일한 메모리 참조
부모 프로세스 ──┐
├──► [물리 메모리: session:user:1001="active"]
자식 프로세스 ──┘
# 쓰기 발생 시: 해당 페이지만 복제 (Copy-On-Write)
부모 프로세스 ──► [물리 메모리 A: session:user:1001="inactive"] ← 새로 할당
X
자식 프로세스 ──► [물리 메모리 B: session:user:1001="active"] ← RDB에 기록됨
따라서 자식 프로세스는 fork() 시점의 데이터 스냅샷을 유지하며 RDB 파일을 완성하고, 메인 스레드는 최신 상태로 계속 운영됩니다.
스냅샷 완료 후의 데이터 일관성
중요한 점은 BGSAVE 실행 도중 메인 스레드가 수정한 데이터는 현재 스냅샷에 포함되지 않는다는 것입니다. 해당 변경사항은 다음 스냅샷에 반영됩니다.
만약 스냅샷 파일 작성이 완료된 직후 서버가 비정상 종료되면, 스냅샷 생성 기간 중 메인 스레드가 수행한 모든 수정은 유실됩니다.
메모리 사용량 고려사항
COW 방식은 효율적이지만 극단적인 상황을 주의해야 합니다. BGSAVE가 진행되는 동안 메인 스레드가 공유된 모든 메모리 페이지를 수정하면, 각 페이지가 복제되어 메모리 사용량이 2배로 늘어날 수 있습니다.
쓰기 집약적인 워크로드 환경에서는 스냅샷 실행 시 메모리 여유를 충분히 확보해야 합니다.
하이브리드 영속화
Redis 4.0부터는 RDB와 AOF의 장점을 결합한 혼합 영속화(Hybrid Persistence)를 제공합니다. 이 기능은 AOF 재작성(rewrite) 과정에서 활성화됩니다.
설정 활성화:
aof-use-rdb-preamble yes
동작 방식은 다음과 같습니다:
- AOF 파일 재작성이 필요하면 자식 프로세스 생성
- 자식 프로세스는 공유 메모리를 RDB 형식으로 먼저 기록
- 재작성 시작 이후 메인 스레드에서 발생한 명령어는 버퍼에 누적
- RDB 기록 완료 후 버퍼의 명령어를 AOF 형식으로 추가
- 완성된 파일로 기존 AOF 교체
결과적으로 생성되는 AOF 파일은 다음 구조를 가집니다:
[RDB 형식 부분: 스냅샷 시점의 전체 데이터]
─────────────────────────────────────
[AOF 형식 부분: 재작성 이후의 증분 명령어]
서버 재시작 시 RDB 부분을 빠르게 로드한 후, AOF 부분의 명령어를 재실행하여 데이터 일관성을 확보합니다. 이 방식은 RDB의 빠른 복구 속도와 AOF의 세밀한 데이터 보존 력을 동시에 얻을 수 있습니다.