콘텐츠로 이동

Data Backup

레디스는 모든 데이터를 메모리에 저장하므로, 예기치 않은 서버 장애나 재시작 시 데이터가 유실될 수 있어, 영속성을 확보하기 위한 데이터 백업 메커니즘이 필요하다.

RDB는 특정 시점의 메모리 상태 전체를 하나의 바이너리 파일(dump.rdb)로 저장하는 스냅샷 방식이다.

스냅샷을 만드는 방법은 두 가지로, 메인 스레드 블로킹 여부가 핵심 차이다.

명령어실행 방식메인 스레드운영 권장
SAVE메인 스레드가 직접 디스크 기록동기 수행, 블로킹사용 지양
BGSAVEfork()로 자식 프로세스 생성 후 자식이 기록비블로킹, 요청 계속 처리표준 방식, 자동 트리거 동일
  • SAVE(Save): 메인 스레드가 모든 키를 순회하며 직접 디스크에 쓰는 동기 명령으로, 데이터셋이 큰 경우 수 초~수십 초 동안 모든 클라이언트 요청 블로킹
  • BGSAVE(BackGround Save): 메인 프로세스를 fork()해 자식 프로세스가 디스크 기록을 담당, 메인 스레드는 그동안에도 정상적으로 클라이언트 요청 처리

운영 환경에서는 사실상 모든 스냅샷이 BGSAVE 또는 그와 동일한 메커니즘으로 동작한다.

  • 자동 스냅샷: redis.confsave 옵션에 설정된 조건이 충족되면 내부적으로 BGSAVE 호출
  • 수동 스냅샷: 관리자가 CLI로 BGSAVE 직접 실행
  • 복제 동기화: 레플리카 노드가 마스터와 최초 동기화 시작 시 마스터가 BGSAVE로 RDB 생성 후 전송

BGSAVE 동작 원리: fork() + Copy-on-Write

섹션 제목: “BGSAVE 동작 원리: fork() + Copy-on-Write”

BGSAVE는 OS의 fork()와 Copy-on-Write 메커니즘을 활용해 메인 스레드를 블로킹하지 않는다.

  1. 메인 프로세스가 fork() 시스템콜로 자식 프로세스를 생성 → 자식은 부모와 동일한 페이지 테이블을 공유하고, 양쪽의 모든 페이지는 읽기 전용으로 마킹됨(물리 페이지는 복사되지 않음)
  2. 자식 프로세스는 fork() 시점의 데이터 스냅샷을 디스크에 쓰는 동안, 부모는 클라이언트 요청을 블로킹 없이 계속 처리
  3. 부모가 공유 페이지에 쓰기를 시도하면 MMU가 읽기 전용 위반을 감지해 페이지 폴트 발생 → 커널이 새 물리 페이지를 할당해 원본을 복사한 뒤 부모의 페이지 테이블만 새 페이지를 가리키도록 갱신
    • 부모는 그제서야 쓰기 성공(Copy-on-Write)
    • 자식의 페이지 테이블은 여전히 원본을 가리키므로 fork() 시점 스냅샷이 그대로 유지
  4. 자식이 디스크 기록을 마치면 종료, 새 RDB 파일이 기존 파일을 원자적으로 교체

BGSAVE를 사용할 때, 다음과 같은 주의점이 있다.

  • 메모리 사용량 폭증 가능성: 쓰기 트래픽이 많으면 CoW로 복사되는 페이지가 늘어나 메모리 사용량이 순간적으로 최대 2배까지 증가 → vm.overcommit_memory=1 권장
  • fork() 자체의 블로킹: 거대한 메모리 공간(수십 GB)을 가진 프로세스에서는 페이지 테이블 복사 비용으로 fork() 호출 자체가 수십~수백 ms 블로킹
  • 장점
    • 데이터 전체가 하나의 파일에 담겨있어 파일 크기가 작고 관리 용이
    • 서버 재시작 시 RDB 파일을 읽는 속도가 AOF보다 빨라 대용량 데이터 복원에 유리
  • 단점
    • 스냅샷은 특정 시점에만 생성되므로, 마지막 스냅샷 이후 발생한 데이터 변경분은 장애 시 유실 가능
    • 큰 데이터셋에서 fork() 비용으로 수십~수백 ms 블로킹 가능

AOF는 레디스 서버에서 실행된 모든 쓰기(write) 명령을 로그 파일에 순차적으로 기록하는 방식이다.

  • 동작 원리

    • 클라이언트로부터 쓰기 요청이 들어올 때마다 해당 명령어를 AOF 파일의 끝에 추가
    • 정확히는 write() 시스템콜로 커널 페이지 캐시에 먼저 기록되고, 실제 디스크 동기화(fsync())는 appendfsync 정책에 따라 별도 타이밍에 수행
    • 서버 재시작 시, 이 파일에 기록된 모든 명령어를 처음부터 순서대로 재실행하여 데이터 복원
  • fsync 정책: appendfsync 옵션으로 조절하며, 이는 데이터 내구성과 성능 간의 트레이드오프를 결정

    • always: 모든 쓰기 명령마다 fsync 수행(가장 안전하지만 성능 저하 심함)
    • everysec(기본값): 1초에 한 번씩 백그라운드 스레드에서 fsync 수행. 장애 시 최대 1초 분량만 유실되고 메인 스레드는 블로킹되지 않아 대부분의 프로덕션에서 기본으로 채택
    • no: fsync를 OS 페이지 캐시 플러시에 맡김(가장 빠르지만 유실 위험 최대)
  • AOF 재구성(Rewrite)

    • AOF 파일은 시간이 지남에 따라 계속 커지므로, BGREWRITEAOF 명령어나 auto-aof-rewrite-percentage 조건으로 파일 크기를 최적화
    • 해당 과정은 현재 메모리 상태를 기반으로 가장 짧은 명령어 조합을 만들어 새로운 AOF 파일을 생성(기존 로그를 읽지 않음)
    • 동시 쓰기 누락 방지 흐름
      1. 부모가 fork()로 자식 생성 → 자식은 현재 메모리 스냅샷 기반으로 새 AOF 파일을 작성
      2. 자식이 새 파일을 쓰는 동안, 부모는 기존 AOF 파일에 계속 기록하면서 “AOF rewrite buffer”에도 같은 명령을 별도 누적
      3. 자식이 새 파일 작성을 완료하면, 부모는 rewrite buffer에 쌓인 명령을 새 파일 끝에 이어 붙여 일관성 확보
      4. 기존 AOF 파일을 새 파일로 원자적 교체(rename)
    • Redis 7의 MP-AOF(Multi-Part AOF): 단일 파일 대신 base + incremental + manifest 다중 파일로 운영
      • base는 rewrite 시점의 RDB 바이너리로 저장되어 rewrite 재시작·디스크 사용량·내구성 모두 개선
  • 장점

    • fsync 정책에 따라 데이터 유실을 최소화할 수 있어 RDB보다 높은 내구성 제공
  • 단점

    • 동일한 데이터라도 RDB 파일보다 파일 크기가 더 클 수 있음
    • 서버 재시작 시 모든 명령어를 재실행해야 하므로 RDB보다 복원 속도가 느릴 수 있음

레디스 서버는 오직 재시작될 때만 데이터를 복원한다.

  1. 서버가 시작되면 먼저 AOF 파일(appendonly.aof)의 존재 여부를 확인
  2. AOF 파일이 존재하면, 해당 파일을 로드하여 데이터를 복원(AOF 파일이 RDB 파일보다 우선)
  3. AOF 파일이 없고 RDB 파일(dump.rdb)만 존재하면, RDB 파일을 로드

데이터의 안정성을 극대화하기 위해 두 가지 방식을 모두 활성화하는 것이 강력하게 권장된다.

  • 평상시 내구성: AOF가 데이터의 실시간 변경 사항을 기록하여 데이터 유실 최소화
  • 주기적 백업 및 빠른 복원: RDB는 주기적으로 전체 데이터의 스냅샷을 만들어두어, 재해 발생 시 특정 시점으로의 빠른 복구 지원
  • 시너지 효과: AOF 재구성 시, 레디스는 가장 최근의 RDB 스냅샷을 활용하여 재구성 속도를 높이고 효율성 극대화

마지막 업데이트:

Redis