본문 바로가기
삵
NoSQL

Redis 재시작했더니 데이터가 사라질 때

빅빅토르최·2026년 10월 3일·조회 1

캐시로만 쓰던 Redis에 어느 순간 세션이나 카운터, 큐처럼 사라지면 곤란한 데이터가 올라가 있는 경우가 많다. 그러다 서버를 한 번 재시작하면 그 데이터가 통째로 비어 있는 걸 보고 당황하게 된다. 저도 현장에서 "Redis 재시작했더니 값이 다 없어졌어요"라는 문의를 여러 번 받았는데, 열에 아홉은 영속화 설정을 한 번도 손대지 않은 상태였다.

결론부터 말하자면, Redis는 기본적으로 메모리 DB이고 재시작 시 복구는 디스크에 저장된 RDB 스냅샷이나 AOF(Append Only File) 로그가 있어야만 가능하다. 둘 다 꺼져 있으면 재시작은 빈 데이터셋으로 올라온다. 데이터 유실을 최소화하려면 AOF를 켜고 appendfsync everysec로 두는 설정이 기준점이 된다.

왜 재시작하면 비어 있나

Redis는 데이터를 메모리에 올려두고 서비스한다. 디스크에 주기적으로 복사본을 남기는 것이 영속화(persistence)인데, 이 복사본이 재시작 때 다시 메모리로 올라오는 재료다. 복사본이 없으면 재시작 후 메모리는 깨끗하게 비어 있는 상태로 시작한다.

Redis가 제공하는 영속화 방식은 두 가지다. 하나는 특정 시점의 데이터셋 전체를 하나의 바이너리 파일로 찍어두는 RDB 스냅샷이고, 다른 하나는 쓰기 명령을 받을 때마다 로그에 덧붙여 재생 가능하게 하는 AOF다. 둘을 함께 켤 수도 있다.

먼저 지금 내 Redis가 어떤 상태인지부터 확인한다.

$ redis-cli info persistence | head -n 20
# Persistence
loading:0
rdb_changes_since_last_save:12
rdb_bgsave_in_progress:0
rdb_last_save_time:1759460000
rdb_last_bgsave_status:ok
aof_enabled:0
aof_rewrite_in_progress:0
aof_last_bgrewrite_status:ok
aof_last_write_status:ok

aof_enabled:0이면 AOF는 꺼져 있다. 여기에 RDB save 규칙까지 비어 있으면 디스크에 아무것도 안 남기는 순수 캐시 모드다. 이 상태에서 재시작하면 당연히 비어 있다.

RDB 스냅샷의 동작과 설정

RDB는 지정한 조건이 맞을 때 데이터셋 전체를 dump.rdb 파일로 덤프한다. 조건은 redis.conf의 save 지시자로 "N초 동안 M개 이상 키가 바뀌면 저장"처럼 적는다.

# redis.conf
save 3600 1
save 300 100
save 60 10000

dbfilename dump.rdb
dir /var/lib/redis

위 예시는 1시간에 1건, 5분에 100건, 1분에 1만 건 변경 중 하나라도 충족되면 백그라운드 저장을 건다. dir는 파일이 저장되는 작업 디렉터리, dbfilename은 파일 이름이다.

저장은 BGSAVE로 수동으로도 건다. 이 명령은 부모 프로세스가 fork()로 자식을 만들고, 자식이 디스크 쓰기를 전담하는 copy-on-write 방식이다. 부모는 그동안 클라이언트 요청을 계속 받는다.

$ redis-cli bgsave
Background saving started

$ redis-cli info persistence | grep rdb_last_bgsave_status
rdb_last_bgsave_status:ok

RDB의 장점은 단일 파일이라 백업과 원격 전송, 대용량 재시작이 빠르다는 점이다. 단점은 분명하다. 마지막 스냅샷 이후에 들어온 쓰기는 비정상 종료 때 그대로 날아간다. save 60 10000 같은 규칙이라면 최악의 경우 직전 1분 가까이의 쓰기를 잃을 수 있다.

여기서 한 번 걸린 적이 있다. 데이터셋이 크면 fork() 자체가 수십에서 수백 밀리초 지연을 만든다. 저장이 돌 때마다 응답 지연이 튄다면 RDB 저장 시점과 데이터 크기를 의심해봐야 한다.

AOF를 켜면 무엇이 달라지나

AOF는 데이터를 바꾸는 모든 쓰기 명령을 Redis 프로토콜 형식 그대로 로그에 덧붙인다. 재시작하면 이 로그를 처음부터 재생해 메모리 상태를 복원한다. 스냅샷보다 유실 구간이 훨씬 짧다는 것이 핵심이다.

켜는 방법은 두 가지다. 운영 중인 인스턴스라면 데이터 유실 없이 전환하기 위해 CONFIG SET으로 켠 뒤 설정 파일에 반영한다. 공식 문서는 설정 파일만 바꾸고 재시작하는 방식을 명확히 경고한다. 그렇게 하면 데이터가 날아갈 수 있다.

$ redis-cli config set appendonly yes
OK
$ redis-cli config rewrite
OK

$ redis-cli info persistence | grep aof_enabled
aof_enabled:1

Redis 7.0.0부터 AOF는 여러 파일로 나뉜 멀티 파트 구조다. 하나의 베이스 파일(스냅샷)과 여러 개의 증분 파일(이후 변경), 그리고 이들을 추적하는 매니페스트 파일로 구성된다. 이 파일들은 appenddirname으로 지정한 디렉터리(기본값 appendonlydir) 안에 모인다.

$ ls -1 /var/lib/redis/appendonlydir/
appendonly.aof.1.base.rdb
appendonly.aof.1.incr.aof
appendonly.aof.manifest

베이스 파일이 .base.rdb 확장자인 건 aof-use-rdb-preamble yes(기본값) 때문이다. 베이스를 RDB 형식으로 압축해 두면 재시작 로딩이 빨라진다. 재시작 시 AOF와 RDB가 둘 다 켜져 있으면 Redis는 더 완전한 쪽인 AOF를 써서 복원한다.

appendfsync로 내구성과 속도를 고른다

AOF 로그를 메모리 버퍼에 쓰는 것과 디스크에 실제로 내려쓰는(fsync) 것은 다른 일이다. appendfsync는 이 fsync 주기를 정한다. 값은 세 가지다.

appendfsync everysec

기본값이다. 1초에 한 번 fsync한다. fsync는 백그라운드 스레드에서 수행되고 메인 스레드는 가능한 한 쓰기를 이어가므로 성능 저하가 작다. 비정상 종료 시 최대 약 1초치 쓰기를 잃을 수 있다. 대부분의 운영에서 이 값이 출발점이다.

appendfsync always

명령 배치마다 fsync한다. 유실은 사실상 없지만 디스크 왕복이 매번 끼어 느리다. 결제 원장처럼 1초의 유실도 허용되지 않는 데이터에만 선택한다. 파이프라인이나 다중 클라이언트의 병렬 쓰기는 그룹 커밋으로 단일 fsync에 묶이기도 한다.

appendfsync no

fsync를 Redis가 강제하지 않고 커널에 맡긴다. 리눅스는 보통 약 30초마다 내려쓴다. 가장 빠르지만 비정상 종료 시 유실 구간이 가장 크다. 유실을 감수할 수 있는 캐시성 데이터가 아니면 권하지 않는다.

# redis.conf
appendonly yes
appendfsync everysec
appenddirname "appendonlydir"

권장을 콕 집자면, 세션과 카운터처럼 날아가면 곤란한 데이터는 everysec로 두고, 1초의 유실도 허용 못 하는 소수의 경우에만 always로 올린다. no는 순수 캐시에만 쓴다.

auto-aof-rewrite로 로그 비대를 막는다

AOF는 쓰기마다 덧붙이기 때문에 계속 커진다. 같은 카운터를 100번 올리면 로그에는 100개의 항목이 쌓이지만 실제로 필요한 건 최종값 하나다. 이 군더더기를 정리하는 것이 AOF 재작성(rewrite)이다. 재작성은 현재 데이터셋을 만드는 데 필요한 최소 명령으로 새 베이스 파일을 만든다.

자동 재작성은 두 지시자로 제어한다. auto-aof-rewrite-percentage는 마지막 재작성 이후 크기가 몇 % 늘면 재작성할지(기본 100, 즉 2배), auto-aof-rewrite-min-size는 그 이하 크기면 재작성하지 않는 하한선(기본 64mb)이다.

# redis.conf
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

수동으로는 BGREWRITEAOF를 건다. RDB 저장이 진행 중이면 Redis는 두 백그라운드 작업이 동시에 디스크를 때리지 않도록 재작성을 예약했다가 스냅샷이 끝난 뒤 시작한다.

$ redis-cli bgrewriteaof
Background append only file rewriting started

$ redis-cli info persistence | grep -E 'aof_rewrite_in_progress|aof_last_bgrewrite_status'
aof_rewrite_in_progress:0
aof_last_bgrewrite_status:ok

percentage를 너무 작게 잡으면 재작성이 잦아져 fork() 부담이 늘고, 너무 크게 잡으면 로그가 비대해져 재시작 로딩이 느려진다. 기본값 100에서 시작해 디스크 I/O와 로딩 시간을 보며 조정한다.

복구와 손상 대응

비정상 종료로 AOF 끝부분이 잘려 나갈 수 있다. 이때 Redis는 기본 설정(aof-load-truncated yes)에서 잘린 마지막 명령만 버리고 나머지를 로드한다. 가용성을 위한 기본 동작이다.

* Reading RDB preamble from AOF file...
* Reading the remaining AOF tail...
# !!! Warning: short read while loading the AOF file !!!
# !!! Truncating the AOF at offset 439 !!!
# AOF loaded anyway because aof-load-truncated is enabled

끝부분이 아니라 중간이 깨진 경우는 다르다. Redis가 시작에 실패하고 백업 후 redis-check-aof --fix를 쓰라고 안내한다. 먼저 --fix 없이 돌려 손상 지점을 확인하고, 수리하면 그 지점 이후가 버려질 수 있으니 반드시 원본을 백업한 뒤 작업한다.

$ cp -a /var/lib/redis/appendonlydir /var/lib/redis/appendonlydir.bak
$ redis-check-aof /var/lib/redis/appendonlydir/appendonly.aof.manifest

백업은 RDB와 AOF가 조금 다르다. RDB는 생성 완료 후 변경되지 않으므로 dump.rdb를 그냥 복사하면 된다. AOF는 재작성 중에 복사하면 깨진 백업이 나올 수 있어, 재작성을 잠시 꺼야 한다.

$ redis-cli config set auto-aof-rewrite-percentage 0
OK
$ redis-cli info persistence | grep aof_rewrite_in_progress
aof_rewrite_in_progress:0
$ tar czf aof-backup.tgz -C /var/lib/redis appendonlydir
$ redis-cli config set auto-aof-rewrite-percentage 100
OK

어느 조합을 쓸까

PostgreSQL에 준하는 데이터 안전성을 원하면 RDB와 AOF를 함께 켠다. AOF로 유실 구간을 1초 안으로 줄이고, RDB 스냅샷으로 빠른 백업과 재시작을 확보하는 조합이다. 공식 문서도 이 둘을 함께 쓰는 방식을 권한다.

몇 분의 유실을 감수할 수 있으면 RDB만으로도 충분하다. 반대로 AOF만 단독으로 쓰는 건 권하지 않는데, 가끔 찍어두는 RDB 스냅샷이 백업과 빠른 재시작, AOF 엔진 버그 대비에 유용하기 때문이다.

세션 저장소, 카운터, 작업 큐처럼 재시작 후에도 남아야 하는 데이터라면 AOF everysec + RDB 조합이 기본 선택이다. 순수 캐시라면 영속화를 끄고 메모리와 속도를 챙기는 편이 맞다. 이 글의 설정값은 Redis 7.0 이상 기준이며, 세부 기본값은 버전과 배포판에 따라 다를 수 있으니 redis-cli config get으로 실제 값을 확인하고 적용하길 권한다.

자주 묻는 질문

Redis를 재시작하면 왜 데이터가 사라지나?

Redis는 메모리에 데이터를 두고 서비스하며, 재시작 시 디스크에 저장된 RDB 스냅샷이나 AOF 로그를 읽어 복원한다. 둘 다 꺼져 있으면 디스크에 남은 복사본이 없어 빈 데이터셋으로 올라온다. redis-cli info persistence에서 aof_enabled와 RDB save 규칙을 먼저 확인해야 한다.

appendfsync everysec과 always는 어떻게 다른가?

everysec은 1초에 한 번 fsync하며 백그라운드 스레드가 처리해 성능 저하가 작고, 비정상 종료 시 최대 약 1초치 쓰기를 잃을 수 있다. always는 명령 배치마다 fsync해 유실이 사실상 없지만 디스크 왕복이 매번 끼어 느리다. 1초의 유실도 허용 못 하는 데이터만 always로 올린다.

RDB와 AOF 중 무엇을 써야 하나?

데이터 안전성이 중요하면 RDB와 AOF를 함께 켠다. AOF로 유실을 1초 안으로 줄이고 RDB로 빠른 백업과 재시작을 확보하는 조합이다. 몇 분의 유실을 감수할 수 있으면 RDB만으로도 충분하다. AOF 단독은 백업과 빠른 재시작 이점이 줄어 권하지 않는다.

auto-aof-rewrite-percentage는 무엇을 제어하나?

마지막 AOF 재작성 이후 파일 크기가 몇 % 늘면 자동 재작성을 걸지 정한다. 기본값 100은 크기가 2배가 되면 재작성한다는 뜻이다. auto-aof-rewrite-min-size(기본 64mb)보다 작으면 재작성하지 않는다. 너무 작으면 fork 부담이 늘고 너무 크면 재시작 로딩이 느려진다.

AOF 파일이 손상되면 어떻게 복구하나?

끝부분만 잘린 경우 기본 설정(aof-load-truncated yes)에서 Redis가 마지막 명령만 버리고 로드한다. 중간이 깨진 경우는 시작에 실패하므로, 먼저 디렉터리를 백업한 뒤 redis-check-aof를 --fix 없이 돌려 손상 지점을 확인하고 수리한다. 수리 시 손상 지점 이후가 버려질 수 있다.

관련 글

댓글 0

로그인 후 댓글을 남길 수 있습니다.

아직 댓글이 없습니다.