본문 바로가기

SearchDeveloper/개발자를 위한 레디스

8장 복제

가용성(Availability)

Availability = Available for Use Time / Total Time

  • 서비스를 정상적으로 사용할 수 있는 시간의 비율
  • 가용성이 높은 시스템을 고가용성 시스템이라고 한다.

레디스에서 고가용성을 확보하는 법

  • 복제: 마스터 노드 데이터를 복제본 노드로 실시간 복사하는 기능. 마스터 장애 나도 데이터는 복제본에서 챙길 수 있음 (8장)
  • 자동 페일오버: 마스터에서 발생한 장애 감지해서 클라이언트 연결을 복제본 노드로 자동으로 redirection하는 기능. 수동으로 레디스 엔드포인트 변경할 필요 없어 빠른 장애 조치 가능 (9장)

레디스에서의 복제 구조

일단 운영중인 서비스에 복제본 노드 추가하는 이유?

  • 하드웨어는 언제든지 고장날 수 있으니 마스터 DB 다운됐을 때 대신 사용할 복제본 필요
  • 부하 분산 용도
  • 마스터에서 백업 작업은 부담스러우니 복제본에서 백업 수행하면 서비스 영향 최소화 가능

레디스는 쓰기 작업은 마스터에서만 하고, 복제본은 읽기 작업만 한다

  • MySQL, PostgreSQL은 멀티 마스터 복제 구조라 모든 노드가 동시에 마스터, 복제본 될 수 있다.
  • 하지만 레디스는 멀티 마스터 구조 지원 안 해서 마스터는 복제본이 될 수 없다.
  • 레디스 2.6부터 복제본 노드는 읽기 전용이라 읽기 커맨드만 수행 가능. 쓰기 커맨드는 모두 마스터 노드에서 하는게 일반적임.

복제 구조 구성하기

복제본 노드에서 커맨드 실행

REPLICAOF 마스터IP 마스터Port
  • 복제 연결 시작

  • 마스터에 여러 복제본이 연결될 수도 있고, 복제본 밑에 새로운 복제본 추가하는 것도 가능
  • 대신 한 개의 복제 그룹에서 마스터는 하나임

패스워드 설정

레디스 6.0 부터 ACL 기능이 아닌 패스워드 설정하려면 설정파일에 넣어주면 됨

마스터 노드

requirepass mypassword # 슬레이브가 접속 시 필요한 비밀번호

복제본 노드

masterauth mypassword  # 마스터 접속 비밀번호

한 복제 그룹에 속한 노드들은 같은 패스워드 사용하는게 일반적이다.

복제 메커니즘

복제는 자동으로 이뤄져서 사용자 개입이 필요하지 않다.

첫 복제 노드 연결 - 버전 7.0이하, repl-diskless-sync: no (default)

1) REPLICAOF로 복제본에서 복제 연결 시도

2) fork()로 나온 자식 프로세스가 RDB 스냅샷 파일 생성 시작

3) RDB 만드는 동안의 변경분은 마스터 복제 버퍼에 RESP(레디스 프로토콜) 형태로 저장됨

4) RDB 파일 생성 완료되면 복제본 노드로 복사됨

5) 복제본 노드에선 모든 내용 삭제하고 RDB 파일로 데이터 로딩

6) 복제 버퍼 RESP도 복제본 노드로 전달시킴

→ 디스크 I/O 처리량에 영향을 받는다. 마스터가 파일로 저장하고, 복제본에서도 파일을 읽기 때문.

7.0 이상에선 디스크 사용하는 방식으로 디폴트 바뀜

첫 복제 노드 연결 - 버전 7.0이상, repl-diskless-sync: yes (default)

1) REPLICAOF로 복제본에서 복제 연결 시도

2) 소켓 통신으로 복제본 노드에 바로 연결, RDB 데이터 생성됨과 동시에 복제본 소켓에 전송

3) RDB 보내는 동안의 변경분은 마스터 복제 버퍼에 RESP(레디스 프로토콜) 형태로 저장됨

4) 복제본에서는 소켓에서 읽은 RDB파일을 디스크에 서장한다.

5) 복제본에서 모든 내용 삭제하고 RDB 파일로 데이터 로딩

6) 복제 버퍼 RESP도 복제본 노드로 전달시킴

→ 네트워크 대역폭이 중요

복제본의 repl-diskless-load: disbled (default) 라 소켓에서 읽어온 RDB 데이터를 바로 메모리에 로드하는게 아니라 일단 디스크에 저장한다.

  • 이유? 메모리 로드 전 모두 삭제 과정을 거쳐야하는데 전송 중간에는 데이터가 정상적인지 미리 확인할 수 없으므로 일단 디스크에 저장하고 나중에 한 번에 올림 (데이터 안전성 확보 목적)

동시 복제 요청에 대해

7.0이하 기존 디스크 사용하는 복제 방식일 경우

  • RDB 파일 생성 도중 다른 노드에서 복제 요청 들어오면 연결은 큐에 저장됨
  • 기존 RDB 파일 생성 완료되면 여러 복제본이 한 번에 복제 연결 시작 가능 (만든 RDB 파일을 한 번에 여러 복제본에 공유한다는 뜻 같음)

디스크 사용하지 않는 복제 방식일 경우

  • 복제 연결 시작된 경우 끝나기 전까지 다른 복제본과의 연결 불가, 큐에서 대기
  • repl-diskless-sync-delay 5 옵션 사용하면 새로 복제 연결 들어오면 5초 후에 복제 연결 시작한단 뜻이라 여러 복제본으로 소켓 통신 연결해 한 번에 여러개 복제본에 RDB 파일 전송할 수 있다. (한 번에 여러 복제 연결 들어오는게 일반적이라 이 옵션 활성화 추천)

복제 연결된 후 - 복제본 데이터 전달은 비동기 방식으로 동작한다

  • 클라 요청을 마스터에서 커맨드 처리하고 클라에 OK 응답 보낸다. 비동기로 복제본에 커맨드 전달한다.
  • 클라는 데이터 입력할 때마다 복제본에 데이터 잘 전달 됐나 확인하는 과정이 않아서 짧은 지연 시간, 높은 성능 갖는다.
    • ES는 레플리카까지 전달되야 동기 응답 함. 레디스는 레이턴시 최소화하기 위해 이런 방식을 선택한 듯
  • 비동기 전송 전에 마스터 비정상 종료되면 데이터 유실 가능성은 있지만 빈번하진 않음

복제 ID

  • 모든 레디스 인스턴스는 복제 ID(replication ID)를 갖고 있다. (복제 기능 사용하지 않더라도)
  • 오프셋은 커맨드 수행할 때마다 증가하는 값이다.
  • 복제ID와 오프셋은 쌍으로 존재한다.
  • INFO REPLICATION 으로 복제 연결 상태 확인 가능

  • 복제본의 replid는 마스터의 replid를 따라간다. 복제본 offset은 마지막으로 수행된 마스터 offset이다.
  • 즉, 복제본의 replidoffset이 마스터와 같아야 두 노드는 정확히 일치된 상태이다.

부분 재동기화(partial resynchronization)

  • 복제 연결이 끊길 때마다 마스터에서 RDB 새로 생성해서 전달하면 네트워크 불안정한 상황에선 성능 나빠질 것
  • 부분 재동기화로 안정적으로 복제 연결 유지한다.

1) 마스터가 커넥션 유실 대비해 백로그 버퍼라는 메모리 공간에 복제본에 전달한 커맨드들 저장해둔다.

2) 복제본 연결이 끊겼다가 재연결됐을 때 복제본은 PSYNC 커맨드를 호출해 자신의 replid, offset을 마스터한테 전달한다.

3) 마스터 백로그 버퍼에 채울 offset데이터가 저장돼있다면 그 offset만 넘김으로써 부분 재동기화를 진행한다.

4) 근데 백로그 버퍼에 원하는 데이터가 안 남아 있거나 복제본이 보낸 replid가 현재 마스터 replid와 일치하지 않다면 전체 재동기화 시도한다.

백로그 버퍼

  • 마스터 백로그 버퍼 크기는 repl-backlog-size (default 1MB) 파라미터로 설정한다. 크기 클수록 부분 재동기화 수행 시간이 늘어날 수 있다.
  • 백로그는 복제본이 1개 이상 연결돼있을 때만 할단된다.
  • repl-backlog-ttl만큼의 시간이 경과하면 메모리에서 백로그 공간 삭제한다.
  • 복제본은 언제든지 마스터로 승격될 수 있어서 복제본에서도 백로그 버퍼가 있고, 연결된 복제본이 없도 유지한다.

Secondary 복제 ID

복제본이 마스터로 승격되는 시나리오

  • 한 복제본 그룹에서 모두 같은 마스터의 replid를 갖지만, 마스터 노드 장애남
  • 마스터 노드와의 복제가 끊어짐과 동시에 복제본은 새 replid를 갖게 된다.
  • B가 마스터노드로 승격되고, B의 replid를 C도 갖게 된다.
  • C가 B에 복제 연결 요청할 때 replid가 같으니까 부분 재동기화를 시도 한다.
  • (만약 A가 다시 돌아왔을 때 master_replid2가 남아있기 때문에 다시 복제 그룹에 합류할 수 있다)

복제본 노드는 읽기 전용 모드로 동작

  • replica-read-only 설정 해제해서 복제본에서 변경 커맨드 수행할 수는 있음
    • 근데 마스터 전체 재동기화하거나 복제본 재시작되면 데이터 사라짐
  • A(마스터) → B(복제본) → C(복제본) 구조일 때 B에서 데이터 변경하더라도 로컬에서만 유지되고 C로 전파되지는 않음
    • 서브 복제본은 항상 최상위 마스터가 중간 복제본으로 보낸 것과 동일한 RESP를 전달받는다.

유효하지 않은 복제본 데이터 대처 (replica-serve-stale-data)

  • 유효하지 않은(stale) 데이터: 복제본과 마스터의 데이터가 정확하게 일치하지 않은 경우의 데이터
    • ex) 복제본과 마스터 연결 끊어진 상태, 복제본 연결 요청 후 아직 완료 안됐을 때
  • 데이터가 stale할 때 읽기 요청 어떻게 대응할 건지 replica-serve-stale-data: yes (default)로 제어 가능
    • yes면 stale해도 클라로부터 읽기 요청 들어왔을 때 데이터 반환 / no면 에러 뱉음

백업을 사용하지 않을 때 복제

복제 사용할거면 백업 키는게 좋음. 백업 안 킬꺼면 재부팅 후 레디스 자동 재시작 옵션 끄는게 좋음

  • 만약 백업 껐을 때, 마스터가 자동 재부팅되면서 메모리 초기화되면 그대로 복제 연결되면서 복제본도 empty RDB 덮어쓰게 됨
  • 그나마 자동 재부팅 설정 끄면 복제본 노드를 마스터로 설정하거나 복제본 노드 데이터를 마스터로 새로 백업받아 복원할 수도 있음