본문 바로가기
데이터베이스

Redis - Replication(복제)

by pius712 2025. 3. 16.

Redis 복제(Replication) 기초

HA(High Availability)를 위해서는 아래 두가지가 필요하다

  • 복제
  • 자동 fail over

이번 글에서는 그중 Redis의 복제 개념을 먼저 살펴보고, 자동 Failover에 대해서는 Sentinel이나 Cluster에서 다루려고 한다.

1. 복제란

redis 노드는 크게 두 가지 역할을 가진다.

  • master
  • replica

Replica는 Master 노드의 데이터를 복사받아, **읽기 전용(read-only)**역할을 수행하며, 쓰기 커맨드는 기본적으로 Replica에서 불가능하다.

(물론 설정을 변경해 Replica에도 쓰기가 가능하게 할 수는 있지만, 권장하지 않는다.)

Redis 는 다른 DB 들과 달리 master 노드는 replica 가 될 수 없다.

1.1 복제의 동작 방식

Redis는 싱글 스레드로 클라이언트 요청(쿼리)을 처리하지만, 복제 작업은 Non-blocking 방식으로 진행된다.

  • 복제가 진행되는 동안에도 Master는 계속 클라이언트 요청을 처리할 수 있다.

Replica 쪽도 대부분의 경우 비동기로 동작하지만,

  • 초기 동기화(특히 전체 복제를 받는 과정)에서 일시적으로 클라이언트를 차단할 수 있다.
  • 초기 동기화 이후 데이터 로드는 메인 스레드에서 이루어져서 짧은 시간 동안 쿼리가 차단된다.

1.2 복제 흐름

  1. 클라이언트는 Master에 명령을 보내고, Master로부터 응답을 받는다.
    • 이때, 동시에 Replica로의 복제(동기화)가 완료될 때까지 클라이언트가 기다리지 않는다.
    • 즉, Master로부터 응답을 받은 시점에 Replica에는 해당 명령이 수행되지 않았을 수 있다. 이를 Replication Lag라고 부르며, 짧은 시간 동안 Master와 Replica가 다른 데이터 셋을 가질 수 있다.
  2. Replica와의 연결이 끊어지면, 부분 재동기화(Partial Resynchronization)를 시도한다.
  3. 부분 재동기화가 불가능한 경우, 전체 재동기화(Full Resynchronization)를 요청한다.

2. 동기화

  1. Replica가 Master에 접속하여 “PSYNC”를 요청합니다.
  2. Master는 Replica가 완전한 데이터 동기화가 필요한 “Full Sync” 상태라고 판단하면,
    • 내부에서 **fork()**를 실행해 백그라운드(child) 프로세스를 생성합니다.
    • 자식 프로세스가 RDB 스냅샷(디스크 기반) 혹은 diskless 복제(네트워크로 바로 전달) 데이터를 만들어서 Replica로 보냅니다.
    • 이때 fork() 때문에 Copy-on-Write(COW) 오버헤드가 발생합니다.

이 과정을 통해 Master는 큰 부하 없이도 병렬로 스냅샷을 떠서 Replica로 전송할 수 있는데, 문제는 fork 시점에 OS 수준에서 메모리를 복사(혹은 COW)에 대한 비용이 들거나, RDB 파일을 디스크에 쓸 경우 I/O 부담이 생길 수 있다.

2.1 전체 동기화

전체 동기화는 Replica가 Master의 전체 데이터를 복제하는 것을 의미한다.

보통 아래 두 상황에서 발생한다.

  • Initial Synchronization: Replica가 Master에 최초로 커넥션을 맺을 때.
  • Full Resynchronization: Replica가 Master와 다시 커넥션을 맺을때, 일부 데이터만으로는 동기화를 맞출 수 없을 때. 즉, 백로그 기반 부분 재동기화가 불가능할 때.

전체 동기화 방법은 크게 두 가지이다.

  • 디스크 사용 (Disk-based)
  • 디스크 사용 X (Diskless)

2.1.1 디스크 사용 (Disk-based)

  1. Master가 백그라운드에서 fork로 자식 프로세스 생성
  2. 자식 프로세스가 RDB 파일을 디스크에 생성
  3. RDB 파일을 Replica 에 전달
  4. Replica 에서 RDB 파일 메모리에 로드
  5. RDB 생성 시점 이후 Master 가 처리한 명령(버퍼링된 명령어) 를 Replica에 전달하여 최신 상태로 맞춤

2.1.2 디스크 사용 X (Diskless)

느린 디스크의 경우, RDB 파일 생성하는 것은 마스터 노드 자원에 부담을 준다.

마스터 노드가 디스크 사용하지 않는 경우는 아래처럼 동작한다.

  1. fork 된 자식 프로세스가 소켓 통신을 통해서 replica 에게 데이터를 전송
  2. replica 가 이를 RDB 파일로 dlatl 저장
  3. 기존 메모리 flush 후 데이터 로드
  4. 마스터가 버퍼링 해둔 커맨드를 전달하여 동기화

참고로 전체 동기화시에는 fork 로 인해 Copy-on-Write 오버헤드가 발생한다.

AWS의 ElasticCache 를 사용하는 경우 forkless 로 동작한다.

정확한 내부 동작은 나도 모름.

디스크를 쓰지 않으므로 전체 I/O 부담이 줄어들지만, Disk-based 복제처럼 이미 만들어진 RDB 파일을 여러 Replica가 동시에 공유할 수 없다.

Disk-based 방식은 복제 진행 중 다른 replica 가 복제 연결시

해당 연결이 큐에 저장되어 있다가, 파일 저장 완료되면 여러 replica 에 복제본을 줄 수 있다.

반면, diskless 방식은 repl-diskless-sync-delay 옵션을 통해서 복제 요청시 일정시간 기다렸다가,

해당 시간내에 다른 복제요청이 오면 한번에 같이 보내는 식으로 동작할 수 있다.

Redis 7 버전 이후에는 기본 설정(repl-diskless-sync = yes)이 Diskless 방식으로 바뀌었다.

참고: Diskless라고 해서 fork()를 하지 않는 것은 아니다. Redis는 기본적으로 복제 시 백그라운드 프로세스를 만들어 데이터를 전송하며, 그 과정에서 fork()로 인한 COW가 어느 정도 발생할 수 있으나, disk 를 사용하지 않기 때문에 I/O 비용을 줄일 수 있다.

다만, AWS ElastiCache 등 매니지드 서비스에서는 내부적으로 “forkless” 최적화를 적용해, 사용자가 체감하는 COW 비용을 최소화하기도 합니다.

2.2 부분 재동기화

Redis는 노드에 replica 가 연결되면, Replication backlog라는 메모리 공간을 할당한다.

이는 부분 재동기화를 위한 것으로 일정 시점 이전까지의 커맨드를 저장하고 있다.

그리고 Replica 는 연결될 때 master의 replica id 와 replica offset 정보를 통해

어떤 master에 연결되었는지, 어디까지 명령 복제가 되었는지 정보를 저장한다.

이 정보를 통해 부분 재동기화가 가능하다.

  • Replica가 Master와 연결이 끊어지면 자동으로 재연결을 시도한다. 이 때, 이미 받은 시점(Offset) 이후의 명령만 백로그에서 꺼내 보내주면 된다.
  • 이를 부분 재동기화라고 부르며, 전체 복제를 재시도하지 않아도 되는 이점이 있다.

만약 Replica가 알고 있는 Master의 replication idoffset 정보가 Master와 불일치하거나,

백로그에 보관된 양보다 더 많은 데이터가 필요하다면, 전체 동기화를 진행한다.

2.3 replication id, offset

  • replication id: 현재 Master 데이터 셋에 대한 히스토리
  • offset: 해당 히스토리의 서로 다른 시점에 대한 표기이다. (같은 데이터 셋이라고 해도, 복제에 따라 서로 다른 시점의 데이터를 가질 수 있음을 의미한다. 즉, 최신의 데이터가 무엇인지 파악하기 위한 논리적 시점)

Master 가 바뀌어서 replica 중 하나가 Master 로 승격시에도, 이전의 Master ID 를 저장해두는데 이것을

secondary replication id 라고 한다.

이를 통해, fail over 가 발생하여 새 Master 승격되어도, Replica 사이에 기존 Master 히스토리가 같은지 확인하여, 부분 재동기화를 진행할 수 있게 된다.

(만약 기존 replication id 가 없다면 전체 재동기화를 해야할지도 모른다)

3. 복제시 유의 사항

3.1 replica 는 기본적으로 read-only

Replica 는 기본적으로 쓰기 커맨드가 비활성화 되어있다. 물론 해당 설정은 해제가 가능하나, replica 에 직접 데이터를 쓰는 경우는 권장되지 않는다. 왜냐하면,

  • 재동기화 시 데이터 유실 가능성이 있고,
  • replica 에 직접 쓰여진 데이터는 하위 replica 로는 전파되지 않는다.

3.2 Persistence 와 함께 사용하기

replica 기능을 사용할 때, 데이터 백업 기능(Persistence)을 사용하는 것이 좋다.

백업 (persistence) 기능을 사용하지 않을거라면 재부팅시 재시작하지 않도록 설정해야한다.

그렇지 않으면 Master 노드가 재부팅 후 재시작하면서 빈 데이터셋을 Replica 에 전달하여, 데이터가 모두 제거될 수 있다.

참고로, 센티널이나 클러스터를 사용하는 경우 자동 fail over 를 통해 replica 에서 master 로 승격이 진행되나,

replica 만 사용하는 경우에는 자동으로 fail over 되지 않는다.