Redis ‐ Redis Data Persistence - thought-corner/backend-roadmap GitHub Wiki
- 데이터를 생성한 프로그램이 종료되더라도, 그 데이터가 사라지지 않고 유지되는 성질을 의미한다.
- 영속성이란, 데이터를 기억하기 위해 메모리 밖(DB, 파일 등)으로 꺼내어 영구히 보관하는 메커니즘을 말한다.
- Redis는 인메모리 저장소라 프로세스가 종료되면 데이터가 사라진다. 이를 보완하기 위해 RDB(스냅샷) 와 AOF(명령 로그) 두 가지 방식을 제공하며, 둘을 함께 쓰는 Hybrid 방식도 있다.
- 다만 이는 완전한 소멸을 막는 장치이지 RDBMS 수준의 내구성 보장은 아니다. 어떤 설정을 쓰든 유실 구간이 존재하며, 그 구간을 얼마나 줄일지와 성능을 맞바꾸는 선택의 문제다.
- Redis는 인메모리 데이터를 주기적으로 파일에 저장하는데 Redis 프로세스가 장애로 인해 종료되더라도 해당 파일을 읽어들이면 이전 상태를 동일하게 복구할 수 있다.
- RDB 방식은 스냅샷 이벤트가 발생할 때마다 저장된다.
- RDB(Snapshot)는 순간적으로 메모리에 있는 내용을 스냅샷을 떠서 Disk에 옮겨 담는 방식이다. 스냅샷을 뜬다는 말은 특정 시점의 메모리에 있는 데이터를 바이너리 파일로 저장한다는 뜻이다.
-
.rdb확장자를 가지며.rdb파일은 AOF 파일보다 사이즈가 작다. 따라서 로딩 속도가 AOF보다 빠르다. - RDB 방식은 메모리 Snapshot을 그대로 저장하기 때문에 서버를 재구동시 Snapshot을 다시 읽으면 되므로 속도가 빠르나, 스냅샷을 뜨는 시점 사이에 장애가 발생하면 마지막 저장 이후의 데이터가 모두 사라진다는 단점이 있다.
- 백업과 복제에도 쓰인다. 파일 하나만 복사하면 되므로 정기 백업에 적합하고, Replica가 Master에 처음 연결될 때의 전체 동기화(Full Resync)도 RDB 스냅샷을 전송하는 방식으로 이루어진다.
1. SAVE(동기 저장)
- 동작 : 현재 실행 중인 Redis 프로세스가 직접 디스크에 데이터를 쓴다.
- 서버 차단(Blocking) : SAVE가 진행되는 동안 Redis는 다른 어떤 클라이언트 요청도 처리할 수 없다.(싱글 쓰레드 기반)
- 위험성 : 데이터가 클수록 그에 비례해 저장 시간이 길어지며, 그동안 서비스는 완전히 먹통이 된다. 그래서 운영 환경에서는 추천하지 않는다.
2. BGSAVE(백그라운드 저장)
- 동작 :
fork()시스템 콜을 호출하여 자식 프로세스를 생성한다. 실제 저장은 이 자식 프로세스가 담당하고 부모 프로세스는 계속해서 사용자 요청을 처리한다. - 핵심 기술 : Copy-on-Write
-
fork를 한다고 해서 메모리를 그대로 복사해서 2배로 쓰는 개념이 아니라 처음에는 부모와 자식이 같은 메모리 페이지를 공유하다가 부모 프로세스에 쓰기 작업이 발생할 때만 해당 메모리 페이지를 복사한다. - 덕분에 메모리 사용량을 효율적으로 관리하면서도 중단 없는 서비스를 제공할 수 있다.
-
- 그렇다고 메모리 부담이 없는 것은 아니다.
- 저장이 진행되는 동안 쓰기가 많이 몰리면 그만큼 페이지가 복제되어 메모리 사용량이 최대 2배까지 치솟을 수 있다. 그래서
maxmemory를 물리 메모리에 꽉 채워 잡으면 BGSAVE 도중 OOM으로 프로세스가 죽을 수 있다. -
fork()자체도 완전히 공짜는 아니다. 페이지 테이블을 복사하는 순간만큼은 메인 프로세스가 멈추며, 데이터가 수십 GB 규모면 이 지연이 수백 ms에 달할 수 있다.
- 저장이 진행되는 동안 쓰기가 많이 몰리면 그만큼 페이지가 복제되어 메모리 사용량이 최대 2배까지 치솟을 수 있다. 그래서
-
LASTSAVE로 마지막 저장 시각을,INFO persistence의rdb_bgsave_in_progress로 진행 여부를 확인할 수 있다.
3. 자동 저장(save 설정)
- 개발자가 일일이 명령어를 치지 않아도 조건에 따라 자동으로 BGSAVE를 실행하는 설정이다.
- 동작 : 조건이 만족되면 Redis가 내부적으로 BGSAVE를 호출한다. 서비스 운영 중 데이터 유실을 최소화하기 위한 안전장치이다.
- 설정 형식은
save <초> <변경 횟수>이며, 기본값은 세 조건이 함께 걸려 있다.
save 900 1 # 900초(15분) 안에 1건 이상 변경되면 저장
save 300 10 # 300초(5분) 안에 10건 이상 변경되면 저장
save 60 10000 # 60초 안에 10000건 이상 변경되면 저장
- 조건 중 하나라도 만족하면 BGSAVE가 실행된다. 즉 변경이 잦을수록 더 자주 저장된다.
-
save ""로 설정하면 자동 스냅샷을 완전히 끌 수 있다.(AOF만 쓰거나, 유실을 감수하는 순수 캐시 용도)
- AOF는 Redis 서버에 전달되는 모든 쓰기(Write) 명령을 순서대로 파일에 저장한다.
- 서버가 재시작되면 이 파일에 기록된 명령들을 처음부터 다시 실행(Replay)하여 데이터를 복구한다. 그래서 파일이 클수록 재시작 시간이 길어진다.
- 기본값은 비활성(
appendonly no)이므로, 쓰려면appendonly yes로 켜야 한다. - 이 때 파일에 기록된 데이터를 실제 디스크로 물리적으로 내려쓰는(Sync) 주기에 따라 세 가지 정책으로 나뉜다.
- 정확히는 두 단계를 거친다. 명령은 먼저 AOF 버퍼에 쌓였다가 OS의 파일 캐시로
write되고, 그 다음 물리 디스크로fsync된다.appendfsync는 이 두 번째 단계의 주기를 정하는 설정이다.
1. appendfsync always
- 동작 : 쓰기 명령이 실행될 때마다 즉시 디스크에 기록한다.
- 장점 : 최대 안정성, 서버가 갑자기 꺼져도 데이터 유실이 거의 없다.
- 단점 : 명령마다 디스크 I/O가 발생하므로 Redis의 최대 장점인 속도가 급격히 느려진다.
2. appendfsync everysec
- 동작 : 명령을 메모리 버퍼에 모아두었다가 1초에 한 번씩 디스크에 쓴다.(Redis의 기본 설정)
- 장점 : 성능과 데이터 보존 사이의 가장 합리적인 타협점. 성능 저하가 적으면서 유실되는 데이터는 최대 1초분으로 제한된다.
3. appendfsync no
- 동작 : Redis가 직접 디스크 동기화를 요청하지 않고, OS가 판단해서 디스크에 쓰도록 맡긴다.(보통 30초 주기)
- 장점 : 최대 성능, 디스크 쓰기 부하가 가장 적다.
- 단점 : 서버 장애 시 최대 수십초 분량의 데이터가 유실될 수 있어 매우 위험하다.
BGREWRITEAOF(파일 압축/재작성)
- AOF는 모든 로그를 다 적기 때문에 시간이 지날수록 파일 크기가 무한정 커진다는 치명적인 단점이 있다.
- 이를 해결하는 방법이 바로 BGREWRITEAOF(Background Rewrite AOF)이다.
- 동작 : 현재 메모리에 있는 데이터를 기반으로 최단 경로 명령어 리스트를 새로 작성한다. 예를 들어 같은 키에
INCR를 100번 실행한 기록은SET key 100한 줄로 줄어든다.
- 백그라운드 처리 : RDB의 BGSAVE처럼
fork()로 자식 프로세스를 만들어 작업을 수행하므로 메인 서버가 멈추지 않는다. 따라서 BGSAVE와 동일한 Copy-on-Write 메모리 부담을 갖는다.
- 자동 실행 : 아래 두 설정이 함께 만족될 때 Redis가 스스로 재작성을 시작한다.
auto-aof-rewrite-percentage 100: 직전 재작성 시점 대비 크기가 100%(2배) 커지면
auto-aof-rewrite-min-size 64mb: 단, 최소 이 크기는 넘어야 한다(작은 파일에 불필요한 재작성 방지)
- RDB와 AOF를 함께 켤 때는 BGSAVE와 BGREWRITEAOF가 동시에 돌지 않도록 Redis가 알아서 조정한다. 두 작업 모두
fork()를 쓰기 때문이다.
- Redis 4.0부터 도입된 하이브리드 영속성 방식은 RDB의 빠른 복구와 AOF의 데이터 안정성을 모두 취하는 메커니즘이다.
- 설정은
aof-use-rdb-preamble yes이며, Redis 4.0부터 기본값으로 켜져 있다.(AOF를 켜면 자동으로 이 방식이 적용된다) - 앞부분 동작 방식 : 재작성이 시작되면 Redis는 현재 메모리에 있는 데이터를 기반으로 바이너리 형태의 RDB 스냅샷을 생성하여 AOF 파일의 앞부분에 작성한다.
- 빠른 로딩 가능 : 텍스트 형태의 AOF 로그보다 훨씬 크기가 작고, 엔진이 해석할 필요 없이 메모리에 바로 넣을 수 있어 로딩 속도가 압도적으로 빠르다.
- 뒷부분 동작 방식 : RDB 스냅샷을 만드는 도중에도 사용자의 새로운 쓰기 명령은 계속 들어오는데 이 최신 명령들은 RDB 뒤에 기존 AOF 텍스트 형태로 붙는다.
- 최소 데이터 손실 : 재작성 도중 발생한 데이터 변화까지 기록해 데이터 손실을 방지한다.
- 결과적으로 파일 하나 안에 [RDB 스냅샷] + [그 이후의 AOF 명령] 구조가 된다. 복구 시에는 앞부분을 한 번에 메모리로 올린 뒤 뒷부분 명령만 재실행하면 되므로, 순수 AOF보다 재시작이 훨씬 빠르다.
- 이 파일은 더 이상 사람이 읽을 수 있는 순수 텍스트가 아니므로, AOF 파일을 직접 열어 확인·수정하던 방식은 쓸 수 없다.
- AOF와 RDB가 모두 켜져 있으면 Redis는 재시작 시 AOF를 우선해서 읽는다. AOF 쪽이 더 최신 상태를 담고 있기 때문이다.
- Redis 7.0부터 AOF는 단일 파일이 아니라 다중 파일 구조로 바뀌었다.
appenddirname(기본appendonlydir) 아래에 기준 파일(base), 증분 파일(incr), 매니페스트(manifest)가 함께 관리된다. - 손상된 AOF는
redis-check-aof --fix, 손상된 RDB는redis-check-rdb로 점검할 수 있다.
| 상황 | 권장 설정 | 이유 |
|---|---|---|
| 순수 캐시(잃어도 무방) | 영속성 끄기(save "", appendonly no) |
디스크 I/O와 fork 부담을 완전히 제거 |
| 캐시지만 재시작 시 예열 비용이 큰 경우 | RDB만 | 파일이 작고 복구가 빨라 워밍업 용도로 충분 |
| 세션·장바구니 등 유실이 곤란한 데이터 | AOF(everysec) + RDB 병행 |
유실 1초 이내로 제한, RDB는 백업·복제용으로 유지 |
| 금전 거래 등 유실이 치명적인 데이터 | Redis 단독으로 쓰지 말 것 |
always로도 완벽하지 않다. 원본은 RDBMS에 두고 Redis는 보조로 |
- 실무 기본값은 AOF(everysec) + RDB 병행이다. 평시 유실 구간은 1초 이내이고, 백업·복제는 RDB 파일로 처리한다.
- 성능이 최우선이고 유실을 감수할 수 있다면 영속성을 아예 끄는 것도 정당한 선택이다. 캐시에 영속성을 켜두면 fork와 디스크 I/O 비용만 치르고 얻는 것이 적을 수 있다.
- 어떤 조합이든 단일 서버의 영속성은 서버 자체가 소실되면 무의미하다. 가용성이 필요하다면 Replica·Sentinel·Cluster 구성이 함께 있어야 한다.