코딩마을방범대

Redis - (7) Redis 데이터 다루기 (RDB/AOF) 본문

💡 백엔드/Redis

Redis - (7) Redis 데이터 다루기 (RDB/AOF)

신짱구 5세 2023. 6. 20. 15:05

 

 

 

Redis는 In-memory DB 임에도 불구하고, 메모리 데이터를 disk에 저장할 수 있는 특징이 있다

그래서 서버가 꺼진 후 restart되더라도, disk에 저장해놓은 데이타를 다시 읽어서 메모리에 로딩하기 때문에 데이타 유실되지 않는다. 

 

이런 Persistent(영속성) 기능은 휘발성 메모리 DB를 데이터 스토어로서 활용한다는 장점이 있지만,

이 기능 때문에 장애의 주 원인이 되기도 한다.

 

redis에서는 데이타를 저장하는 방법이 RDB (snapshotting) 방식과 AOF (Append only file) 두가지가 있다. 

 

  • RDB 방식은 특정한 각격마다 메모리에 있는 레디스 데이터 전체를 디스크에 쓰는 것이다. (백업에 용이) 
  • AOF 방식은 명령이 실행될때 마다 데이터를 파일에 기록하여 데이터의 손실이 거의 없다.

 

 

 

 

 


 

 

 

 

RDB (snapshotting) 방식

메모리에 있는 내용을 스냅샷을 떠서 DISK에 옮겨 담는 방식

스냅샷을 뜬다
특정 시점의 메모리에 있는 데이터를 바이너리 파일로 저장한다는 뜻

 

직접 세팅하지 않더라도 Redis는 자동으로 .rdb 라는 확장자의 파일에 인메모리 데이터를 저장하도록 디폴트 설정되어 있어,

( 특정 시점의 메모리에 있는 모든 데이터를 바이너리 파일로 저장 )

Redis 프로세스가 장애로 인해 종료되더라도 해당 파일을 읽어들이면 이전의 상태를 동일하게 복구할 수 있다.

 

 

장점

  • 메모리의 snapshot을 그대로 저장하기 때문에 서버를 재구동시할 때 snapshot을 다시 읽으면 되므로 속도가 빠르다.

 

단점

  • snapshot을 추출하는데 시간이 오래걸리고 도중 서버가 꺼지면 이후의 데이터가 모두 사라진다.
    ( 실제로 SAVE 옵션으로 50GB 의 메모리 상태를 저장한다면 7 ~ 8분 정도 소요된다고 한다. )

 

 


 

RDB 설정 하기

$ sudo vi /etc/redis/redis.conf
설정 항목 예시 설명
save 900 1 900초(15분) 이후 1개의 쓰기 발생
save 300 10 300초(3분) 이후 10개의 쓰기 발생
save 60 10000 60초 이후 10,000개의 쓰기 발생 시 디스크에 데이터 복제
stop-writes-onbgsave-error yes
  • RDB 스냅샷 중 쓰기요청 수행 중단옵션.
  • 레디스 서버 및 Disk 지속성에 대한 적절한 모니터링 체계를 갖췄다면 ‘no’로 지정해 사용가능
rdbcompression yes dump.rdb 파일을 LZF로 압축
rdbchecksum yes
  • CRC64로 Checksum 값 생성후 dump.rdb 파일에 추가
  • 데이터영속성 보장이 강화되나 10% 정도의 성능오버헤드 발생
dbfilename dump.rdb 변경 지정 가능
dir ./ dump.rdb 파일과 AOF 파일 생성 위치

 

 

저장 시점 설정하기

SAVE 조건은 여러 개를 지정할 수 있고, 모두 or 조건이다. 

즉, 어느 것 하나라도 만족하면 저장한다.

 

RDB 저장 방식에는 SAVE와 BGSAVE 두 가지가 있다.

  • SAVE              :  순간적으로 redis의 동작을 정지시키고 그 snapshot를 디스크에 저장.(blocking 방식)
  • BGSAVE        :  백그라운드 SAVE 라는 의미로 별도의 자식 프로세스를 띄운 후, 명령어 수행 당시의 snapshot을 disk에 저장하고, redis는 동작을 멈추지 않게 된다. (non-blocking 방식)
SAVE 동작순서
1) Main process가 데이터를 새 RDB temp 파일에 쓴다.
2) 쓰기가 끝나면 기존 파일을 지우고, 새 파일로 교체한다.

BGSAVE 동작 순서
( fork를 하기 때문에 메모리를 거의 두배 가량 사용 )
1) Child process를 fork() 한다.
2) Child process는 데이터를 새 RDB temp 파일에 쓴다.
3) 쓰기가 끝나면 기존 파일을 지우고, 이름을 변경한다.
# save [Seconds] [Changes]
save 900 1                      # 900초(15분) 동안 1번 이상 key 변경이 발생하면 저장
save 300 10                    # 300초(5분) 동안 10번 이상 key 변경이 발생하면 저장
save 60 10000                # 60초(1분) 동안 10000번 이상 key 변경이 발생하면 저장

 

 

RDB 파일명 지정

dbfilename dump.rdb

 

 

RDB 저장 실패시 데이터 읽기 여부

RDB 파일 저장이 실패했을 경우 데이터를 받아 들일지 말지를 정하는 파라미터

( save 이벤트에만 해당, BGSAVE 명령을 직접 입력했을 경우에는 해당하지 않음 )

stop-writes-on-bgsave-error yes
설명
yes
( default )
레디스는 RDB 파일을 디스크에 저장하다 실패하면, 모든 쓰기 요청을 거부
쓰기에 문제가 발생했으니, 빨리 조치를 취하라는 의미
no 디스크 저장에 실패하더라도, 레디스는 쓰기 요청을 포함한 모든 동작을 정상적으로 처리
디스크 쓰기에 실패하는 경우
여유 공간이 부족하거나, 권한(permission) 부족, 디스크 물리적 오류 등

 

 

수동 SAVE

BGSAVE 또는 SAVE 명령어를 실행하면 된다.

> BGSAVE
Background saving started

 

 

 

 

 


 

 

 

 

AOF (Append Only File) 방식

write/update 연산 자체를 모두 log 파일에 기록하는 방식

 

 

default로 appendonly.aof 파일에 기록되며, 조회를 제외한 입력/수정/삭제 명령이 실행될 때 마다 기록된다.

그리고 서버가 재시작될 때, log에 기록된 write/update 연산을 재 실행하는 형태로 데이터를 복구하는 방식이다.

 

  1. 클라이언트가 Redis 에 업데이트 관련 명령을 요청한다. 
  2. Redis 는 해당 명령을 AOF에 저장한다.
  3.  파일쓰기가 완료되면 실제로 해당 명령을 수행해서 Redis 메모리에 내용을 변경한다.

 

RDB 방식과는 달리 특정 시점이 아니라 항상 현재 시점까지의 로그를 기록할 수 있으며, 기본적으로 non-blocking 으로 동작 된다.

 

 

장점

  • AOF는 log 파일에 대해서만 append하기 때문에 log write 속도가 빠르고 어떤 시점에 서버가 다운되더라도 데이터가 사라지지 않는다.
  • RDB는 바이너리 파일이라서 수정이 불가능했지만, AOF 로그 파일은 text 파일이므로 편집이 가능하다.
Redis 데이터 복구 시나리오
실수로 flushall 명령으로 메모리에 있는 데이터를 모두 날렸을 때,
레디스 서버를 shutdown하고 appendonly.aof 파일에서 flushall 명령을 제거 한 후 레디스를 다시 시작하면 데이터 손실없이 데이터를 살릴 수 있다.

 

단점

  • 모든 write/update 연산을 log파일에 남기기 때문에 log 데이터 양이 굉장히 크다.
  • 복구 시 저장된 모든 write/update연산을 다시 실행하기 때문에 재시작 속도가 느린 단점이 있다.
    ( 예를 들어 set 명령이 key는 같고 값을 다른 조건에서 여러번 수행되었다고 하면, 메모리에는 마지막 수행된 값만 남아있지만, AOF에는 수행된 모든 기록이 남아있기 때문 )
그렇기에 AOF 방식은 rewrite 기능을 제공하여 특정 시점에 데이터 전체를 다시 쓰는 기능이 있다.
rewrite를 수행하면 이전 기록은 모두 사라지고 최종 데이터만 기록되기에 파일의 크기가 줄어들게 된다.

 

 


 

AOF 설정 하기

 

AOF(APPEND ONLY MODE) 항목

설정항목 예시 설명
appendonly yes AOF 파일 사용 여부
appendfilename "appendonly.aof" AOF 파일 이름 지정
appendfsync always 운영체제의 fsync( )에 의한 지연된 쓰기 옵션 (메모리 -> Disk) 
- no              :fsync()사용없이 즉시 쓰기, 가장 빠름.
- always      :항상 fsync( ) 사용, 느리지만 안전함.
- everysec   :매 초 마다 fsync( )를 호출
no-appendfsync-on-rewrite no 쓰기 명령에 대한 fsync( ) 리턴 지연 시간이 너무 길어지면 운영체제의 간섭이 있을 수 있으므로, 
스냅샷 생성을 위한 BGSAVE 또는 AOF 파일 기록을 위한 BGREWRITEAOF가 실행되고 있을 때는 fsync( )의 호출을 블록킹 함
auto-aof-rewrite-percentage 100 AOF 파일 초기화 및 재기록 시작을 위해 최초 AOF 파일의 크기를 기준으로 사용 비율을 지정
(‘0’으로 지정 시 AOF 파일 초기화 없음)
auto-aof-rewrite-min-size 64mb AOF 파일 초기화 및 재기록 시작을 위해 파일 크기를 지정
(‘0’으로 지정 시 AOF 파일 초기화 없음)

 

 

redis.conf 설정 방식

레디스는 서버가 시작될 때 데이터 파일을 읽는다.

이때 어떤 데이터 파일을 읽을지는 redis.conf 설정 파일의 appendonlyfile 설정을 따르게 된다.

appendonly yes
설명
yes aof 파일을 읽음
no
( default )
rdb 파일을 읽음

 

 

AOF 파일명 지정

Append only file 명을 지정하는 파라미터

( 이 파라미터는 appendonly가 yes일때 적용됨 )

appendfilename "appendonly.aof"

 

 

AOF에 기록되는 시점 지정

appendonly 파일에 데이터가 쓰여지는 시점을 정하는 파라미터

appendfsync everysec
설명
always 명령 실행 시 마다 AOF에 기록
( 데이터 유실은 거의 없지만 성능이 매우 떨어짐 )
everysec 1초마다 AOF에 기록
( 권장 )
no AOF에 기록하는 시점을 OS가 정함(일반적으로 리눅스의 디스크 기록 간격은 30초)
( 데이터가 유실될 수 있음 )

 

 

AOF에 기록되는 시점 지정

AOF 파일의 상태가 특정 조건(파일 사이즈가 얼마 이상) 일 때 AOF 파일을 현재 상태에 맞춰서 설정에 따라 덮어쓰기 하거나 새로 생성된다.

처음 레디스 서버가 시작할 시점의 AOF 파일 사이즈가 100% 이상 커지면 rewrite 하게 되어있다.
만약 레디스 서버 시작 시 AOF 파일 사이즈가 0이었다면, auto-aof-rewrite-min-size를 기준으로 rewrite 한다.

하지만, min-size가 64mb 이하이면 rewrite를 하지 않는데, 이는 파일이 작을 때 rewrite가 자주 발생하는 것을 방지하기 위함이다.
# AOF 파일 사이즈가 특정 퍼센트 이상 커지면 rewrite 한다.
# 비교 기준은 레디스 서버가 시작할 시점의 AOF파일 사이즈이다.
# 0으로 설정하면 rewrite를 하지 않는다
auto-aof-rewrite-percentage 100
# AOF 파일 사이즈가 64mb 이하면 rewrite를 하지 않는다.
# 파일이 작을때 rewrite가 자주 발생하는 것을 막아준다.
auto-aof-rewrite-min-size 64mb

 

 


 

AOF 파일을 이용한 복구하기

redis cli를 실행하고 키 a,b,c에 값을 대입한 뒤 flushall 커맨드를 실행해 데이터를 모두 지워버렸다.

> set a 11
OK
> set b 22
OK
> set c 33
OK

> keys *
1) "b"
2) "a"
3) "c"

> flushall
OK

> keys *
(empty list or set)

 

 

appendonly.aof  파일 형식

*             : 명령 시작을 나타낸다. 숫자는 명령과 인수의 개수이다.
$            : 명령이나 인수, 데이터의 바이트 수이다. (한글은 UTF8로 했을 경우 한 글자에 3바이트)
*2
$6
SELECT
$1
0
*3
$3
set
$1
a
$2
11
*3
$3
set
$1
b
$2
22
*3
$3
set
$1
c
$2
33
*1
$8
flushall

문제의 flushall 부분을 지워준 후 저장하여 재실행 시 데이터가 복구되어 있음을 확인할 수 있다.

 

 

 

 

 

 


 

 

 

RDB vs AOF 선택 기준

 

RDB 사용 주의할 점

Redis에 장애가 발생했을 때 백업 시점을 제외한 중간 시점에서 발생한 데이터는 유실될 수 있다.

 

첫 번째 백업 후, 두 번째 백업 전에 장애가 발생했을 경우 첫 번째 이후의 데이터들은 유실되는 것이다.

그리고 rdb 파일을 생성하는 cli 명령어 save 는 single thread로 수행하기 때문에 작업이 완료되기 까지 모든 요청이 대기하게 된다.

 

따라서 bgsave 커맨드로 background 자식 프로세스를 통해 RDB 작업 수행하도록 할 것을 권장되는 편이다. 

그러나 bgsave 커맨드 수행 시엔 memory 사용률 조심해야 된다.

 

 

 

redis 서비스에서 사용중인 데이터는 모두 메모리 위에 있는데 이를 “서비스 영향 없이” 스냅샷으로 저장하기 위해서는 Copy-on-Write(COW) 방식을 사용한다.
자식 프로세스 fork() 후 부모 프로세스의 메모리에서 실제로 변경이 발생한 부분만 복사하게 되는데,

만일 write 작업이 많아서 부모 페이지 전부에 변경이 발생하게 되면 부모 페이지 전부를 복사하게 되는 현상이 발생하게 된다.

 

예를들어, 서버 memory 10GB, redis memory 6GB 사용하는 서비스에서 RDB 수행 중 Copy-on-Write(COW)를 실행하여백업하는데 메모리 두배가 필요하다고 하자.

그러면 2GB 가 부족한 상황 오게 되는데 그러면 swap 이 발생하여 서비스 지연이 발생하게 되므로, redis의 max-memory 설정을 주의 깊게 해야 된다.

 

 


 

AOF 사용 주의할 점

예를 들어 100번의 Increment 작업을 통해 0의 데이터를 100으로 만들었다고 생각해보자.

 

그렇다면 최종적으로 저장되어 있는 데이터는 100이지만, AOF 방식에서 기록된 파일을 읽어와 복구하려면 불필요하게 100번의 Increment 쓰기 작업을 무식하게 실행해야만 한다.

 

뿐만 아니라 RDB 방식에 비해 백업 데이터가 크기도 하고, 서버 자원 또한 많이 잡아먹는 편이다.

 

 


 

RDB vs AOF 선택

우선 redis를 캐시로만 사용한다면 "굳이" 백업 기능은 필요 없다. 저장 공간 낭비가 될 수 있다.

 

RDB 

백업은 필요하지만 어느 정도의 데이터 손실이 발생해도 괜찮은 경우

redis.conf 파일에서 SAVE 옵션을 적절하게 변경해서 사용

SAVE 900 1 # 900초 동안 1개 이상 키가 변경되었을 때 RDB 파일 재작성

 

AOF

장애 상황 직전까지 모든 데이터가 보장되어야 할 경우

appendfsync everysec

 

 

그렇지만 RDB와 AOF 방식의 장단점을 상쇄하기 위해서 두가지 방식을 혼용해서 사용하는 것이 바람직하다.

 

주기적으로 RDB(snapshot)으로 백업하고, 다음 snapshot까지의 저장을 AOF 방식으로 수행하는 식으로 혼용한다.

이렇게 하면 서버가 restart될 때 백업된 snapshot을 reload하고, 비교적 적은 양의 AOF 로그만 replay하면 되기 때문에, restart 시간을 절약하고 데이타의 유실을 방지할 수 있다.

 

 

 

 

 


참고사이트

[REDIS] 📚 캐시 데이터 영구 저장하는 방법 (RDB / AOF)

 

 

 

SMALL