Theme:

Redis에 저장한 데이터는 서버가 꺼지면 전부 사라질까요? 인메모리 데이터베이스인데 어떻게 데이터를 보존할 수 있는 걸까요?

Redis는 메모리 기반 데이터 저장소이지만, 영속성(Persistence) 메커니즘을 통해 데이터를 디스크에 보존할 수 있습니다. 그중 AOF(Append Only File)는 모든 쓰기 명령을 순서대로 기록하는 방식으로, RDB 스냅샷과 함께 Redis의 핵심 영속성 전략입니다.

이 글에서는 AOF의 동작 원리, fsync 정책별 차이, rewrite 메커니즘, 그리고 Redis 7.0에서 도입된 혼합 모드까지 정리합니다.


AOF란 무엇인가

AOF는 Append Only File의 약자입니다. Redis 서버에 들어오는 모든 쓰기 명령(SET, HSET, DEL 등)을 파일 끝에 순차적으로 추가(append)하는 방식입니다.

핵심 아이디어는 단순합니다:

  • 데이터의 "현재 상태"가 아니라 **"상태에 도달하기까지의 모든 명령"**을 기록합니다
  • 서버가 재시작되면 AOF 파일의 명령을 처음부터 순서대로 재실행(replay)하여 데이터를 복원합니다
  • 파일에는 Redis 프로토콜(RESP) 형식으로 명령이 저장됩니다
PLAINTEXT
# AOF 파일 내용 예시 (RESP 형식)
*3
$3
SET
$4
name
$5
redis

*3
$3
SET
$3
age
$2
10

위 내용은 SET name redisSET age 10 두 명령이 기록된 것입니다.


왜 AOF가 필요한가

Redis의 또 다른 영속성 방식인 RDB(스냅샷)와 비교하면 AOF의 필요성이 명확해집니다.

비교 항목RDBAOF
저장 방식특정 시점의 메모리 스냅샷모든 쓰기 명령을 순차 기록
데이터 유실마지막 스냅샷 이후 데이터 유실 가능설정에 따라 최대 1초 이내 유실
파일 크기작음 (바이너리 압축)큼 (명령이 누적)
복구 속도빠름느림 (명령 재실행)
가독성바이너리라 읽기 어려움텍스트라 읽기 쉬움

RDB는 주기적으로 스냅샷을 찍기 때문에, 스냅샷 사이에 발생한 쓰기 명령은 유실될 수 있습니다. 반면 AOF는 모든 쓰기를 기록하므로 데이터 유실을 최소화할 수 있습니다.

실무에서 AOF를 선택하는 대표적인 경우:

  • 데이터 유실을 최소화해야 하는 서비스 (결제, 세션 등)
  • 장애 복구 시 최대한 최신 상태로 복원해야 하는 경우
  • RDB 스냅샷의 fork 비용이 부담스러운 대용량 인스턴스

AOF의 내부 동작

쓰기 흐름

AOF의 쓰기 과정은 크게 세 단계로 나뉩니다:

  1. 명령 실행: 클라이언트의 쓰기 명령이 메모리에 반영됩니다
  2. 버퍼에 추가: 실행된 명령이 AOF 버퍼(aof_buf)에 RESP 형식으로 추가됩니다
  3. 디스크 동기화: 설정된 정책에 따라 버퍼 내용이 디스크에 기록(fsync)됩니다
PLAINTEXT
클라이언트 → [SET key value]

        메모리에 반영

        AOF 버퍼에 추가

        fsync 정책에 따라 디스크 기록

여기서 핵심은 3번째 단계의 fsync 정책입니다. 이 정책이 성능과 안전성의 균형을 결정합니다.

appendfsync 옵션

redis.confappendfsync 설정으로 디스크 동기화 주기를 제어합니다.

always

CONF
appendfsync always
  • 모든 쓰기 명령마다 즉시 fsync()를 호출합니다
  • 데이터 안전성: 최고. 최대 1개 명령만 유실 가능
  • 성능: 최저. 매번 디스크 I/O가 발생하므로 throughput이 크게 떨어집니다
  • 사용 사례: 데이터 유실이 절대 허용되지 않는 극히 제한적인 경우

everysec (기본값)

CONF
appendfsync everysec
  • 1초마다 백그라운드 스레드가 fsync()를 호출합니다
  • 데이터 안전성: 좋음. 최대 1초 분량의 데이터 유실 가능
  • 성능: 양호. always보다 훨씬 빠르면서도 합리적인 안전성
  • 사용 사례: 대부분의 프로덕션 환경에서 권장되는 옵션

no

CONF
appendfsync no
  • Redis가 직접 fsync()를 호출하지 않고 OS에 위임합니다
  • 리눅스의 경우 보통 30초마다 OS가 플러시합니다
  • 데이터 안전성: 낮음. OS 버퍼에 있는 데이터가 유실될 수 있음
  • 성능: 최고. 디스크 I/O 부담이 가장 적음
  • 사용 사례: 캐시 용도로만 사용하여 데이터 유실이 무관한 경우

공부하다 보니 everysec가 가장 균형 잡힌 선택이라는 걸 알게 되었습니다. 실제로 Redis 공식 문서에서도 이 옵션을 기본값으로 권장합니다.


AOF Rewrite — 파일이 무한히 커지는 문제 해결

문제: AOF 파일의 비대화

AOF는 모든 명령을 기록하기 때문에 시간이 지나면 파일이 계속 커집니다. 예를 들어:

PLAINTEXT
SET counter 1
INCR counter      # counter = 2
INCR counter      # counter = 3
INCR counter      # counter = 4
DEL counter
SET counter 100

이 6개 명령의 최종 결과는 counter = 100이지만, AOF에는 6줄이 모두 남아 있습니다. 이렇게 중간 과정이 쌓이면 파일이 불필요하게 커집니다.

해결: BGREWRITEAOF

AOF Rewrite는 현재 메모리 상태를 기준으로 최소한의 명령으로 새 AOF 파일을 생성합니다.

BASH
# 수동 실행
redis-cli BGREWRITEAOF

Rewrite 과정:

  1. Redis가 자식 프로세스를 fork합니다
  2. 자식 프로세스가 현재 메모리 상태를 읽어 새 AOF 파일을 작성합니다
  3. Rewrite 중에 들어오는 새 쓰기 명령은 AOF rewrite 버퍼에 별도로 쌓입니다
  4. 자식 프로세스가 완료되면, rewrite 버퍼의 내용을 새 AOF 파일 끝에 추가합니다
  5. 새 AOF 파일이 기존 파일을 원자적으로(atomically) 대체합니다
PLAINTEXT
[메인 프로세스]          [자식 프로세스]
     |                       |
     | -- fork ----------->  |
     |                       | 메모리 → 새 AOF 파일 작성
     | 새 명령 → rewrite 버퍼 |
     |                       | 작성 완료
     | rewrite 버퍼 → 새 AOF  |
     | 기존 AOF → 새 AOF 교체  |

자동 Rewrite 설정

CONF
# AOF 파일이 마지막 rewrite 이후 100% 이상 커졌을 때
auto-aof-rewrite-percentage 100

# 최소 파일 크기 (이보다 작으면 rewrite하지 않음)
auto-aof-rewrite-min-size 64mb

예를 들어 마지막 rewrite 후 AOF가 64MB였다면, 128MB가 되었을 때 자동으로 rewrite가 트리거됩니다.


혼합 영속성 — RDB + AOF (Redis 7.0+)

Redis 7.0부터는 aof-use-rdb-preamble 옵션이 기본으로 활성화되어, AOF rewrite 시 혼합 형식을 사용합니다.

동작 방식

CONF
aof-use-rdb-preamble yes  # Redis 7.0+ 기본값

AOF rewrite가 실행되면:

  1. 새 AOF 파일의 앞부분에 현재 메모리 상태를 **RDB 형식(바이너리)**으로 저장합니다
  2. rewrite 이후 들어온 명령은 뒷부분에 기존 AOF 형식(텍스트)으로 추가합니다
PLAINTEXT
┌─────────────────────────────────┐
│   RDB 형식 (바이너리 스냅샷)      │  ← rewrite 시점의 전체 상태
├─────────────────────────────────┤
│   AOF 형식 (RESP 명령)           │  ← rewrite 이후의 새 명령들
└─────────────────────────────────┘

혼합 모드의 장점

  • 복구 속도 향상: RDB 부분은 바이너리로 빠르게 로드하고, 나머지 AOF 명령만 재실행합니다
  • 파일 크기 감소: RDB 부분이 압축되어 순수 AOF보다 작습니다
  • 데이터 안전성 유지: rewrite 이후 명령은 여전히 AOF로 기록됩니다

순수 AOF 방식에서 복구가 느렸던 문제를 RDB의 빠른 로딩으로 해결한 셈입니다.

Redis 7.0의 Multi-Part AOF

Redis 7.0에서는 AOF 파일 관리 방식이 크게 바뀌었습니다. 기존에는 하나의 AOF 파일이었지만, 이제는 여러 파일로 분리됩니다:

PLAINTEXT
appendonlydir/
├── appendonly.aof.1.base.rdb    # 베이스 파일 (RDB 형식)
├── appendonly.aof.1.incr.aof    # 증분 파일 1
├── appendonly.aof.2.incr.aof    # 증분 파일 2
└── appendonly.aof.manifest      # 매니페스트 (파일 목록 관리)
  • base 파일: rewrite 시 생성되는 기본 스냅샷 (RDB 형식)
  • incr 파일: rewrite 이후 누적된 명령들
  • manifest 파일: 어떤 파일을 어떤 순서로 로드할지 관리

이 구조 덕분에 rewrite 중 장애가 발생해도 기존 파일이 손상되지 않습니다.


AOF 설정 실전 예제

기본 설정

CONF
# AOF 활성화
appendonly yes

# AOF 파일 저장 디렉토리 (Redis 7.0+)
appenddirname "appendonlydir"

# fsync 정책
appendfsync everysec

# 자동 rewrite 설정
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 혼합 모드 (Redis 7.0+ 기본값)
aof-use-rdb-preamble yes

AOF 파일 검증 및 복구

AOF 파일이 손상되었을 때 redis-check-aof 도구로 검증하고 복구할 수 있습니다.

BASH
# AOF 파일 검증
redis-check-aof appendonly.aof

# 손상된 AOF 파일 복구 (잘린 부분 제거)
redis-check-aof --fix appendonly.aof

# Redis 7.0+ Multi-Part AOF 검증
redis-check-aof --fix appendonlydir/appendonly.aof.manifest

상태 모니터링

BASH
# AOF 관련 정보 확인
redis-cli INFO persistence

주요 확인 항목:

PLAINTEXT
aof_enabled:1                    # AOF 활성화 여부
aof_rewrite_in_progress:0        # rewrite 진행 중 여부
aof_last_rewrite_time_sec:2      # 마지막 rewrite 소요 시간
aof_current_size:12345678        # 현재 AOF 파일 크기
aof_base_size:6000000            # 마지막 rewrite 후 크기
aof_buffer_length:0              # AOF 버퍼 크기

RDB와 AOF, 어떻게 조합할까

실무에서는 RDB와 AOF를 함께 사용하는 경우가 많습니다. 각 조합의 특성을 정리합니다.

RDB만 사용

CONF
save 3600 1        # 1시간마다 스냅샷
appendonly no
  • 백업이 간편하고 복구가 빠릅니다
  • 마지막 스냅샷 이후 데이터 유실 가능
  • 캐시 용도에 적합

AOF만 사용

CONF
save ""            # RDB 비활성화
appendonly yes
appendfsync everysec
  • 데이터 유실 최소화 (최대 1초)
  • 복구 속도가 RDB보다 느림
  • 파일 크기가 상대적으로 큼

RDB + AOF 동시 사용 (권장)

CONF
save 3600 1
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes
  • Redis 재시작 시 AOF를 우선으로 로드 (더 완전한 데이터)
  • RDB는 백업/복제 용도로 활용
  • 혼합 모드로 복구 속도까지 확보

공식 문서에서도 두 가지를 함께 사용하는 것을 권장합니다. AOF로 안전성을 확보하고, RDB로 빠른 백업과 복제를 지원하는 구성이 가장 실용적입니다.


주의할 점

AOF를 사용할 때 알아두면 좋은 점들입니다:

  • 디스크 공간 모니터링: AOF 파일이 예상보다 빠르게 커질 수 있습니다. auto-aof-rewrite-percentage를 적절히 설정해야 합니다
  • rewrite 중 메모리 사용량: fork 시 COW(Copy-on-Write)로 인해 일시적으로 메모리 사용량이 증가할 수 있습니다
  • fsync와 성능 트레이드오프: always는 안전하지만 느리고, no는 빠르지만 위험합니다. 대부분 everysec가 적절합니다
  • AOF 파일 직접 수정 금지: 잘못된 편집은 데이터 손상으로 이어집니다. 반드시 redis-check-aof로 검증해야 합니다

정리

AOF의 핵심 포인트를 요약하면:

  • AOF는 모든 쓰기 명령을 순차적으로 기록하여 데이터 유실을 최소화하는 영속성 방식입니다
  • appendfsync everysec가 성능과 안전성의 가장 좋은 균형점입니다
  • AOF Rewrite(BGREWRITEAOF)로 파일 크기를 관리하며, 자식 프로세스를 fork하여 백그라운드에서 수행합니다
  • Redis 7.0+의 혼합 모드는 RDB의 빠른 로딩과 AOF의 안전성을 결합합니다
  • 실무에서는 RDB와 AOF를 함께 사용하는 것이 권장됩니다

기억하기 좋은 한 줄: **"AOF는 명령의 일기장이고, rewrite는 그 일기장을 요약하는 것"**입니다.

댓글 로딩 중...