Database/Mysql

[Real MySQL] 17-2. InnoDB 클러스터: 그룹 복제

noahkim_ 2024. 9. 7. 20:23

백은빈, 이성욱 님의 "Real MySQL" 책을 정리한 포스팅 입니다.

 

1. 그룹 복제

바이너리 로그

  • 내부적으로 Row 포맷의 바이너리 로그와 릴레이로그, GTID를 사용함

 

클러스터 형태

  • 복제에 참여하는 MySQL 서버들이 하나의 복제그룹으로 묶인 클러스터 형태를 가짐
    • 그룹 내 서버들은 서로 통신하면서 양방향으로도 복제를 처리할 수 있음
    • 즉, 하나의 복제 그룹 내에서 쓰기를 처리하는 서버가 여러 대 존재할 수 있음
    • 기존 복제의 경우 소스-레플리카 형태로 구성되어 단방향으로만 복제가 이루어졌음
  • 프라이머리-세컨더리라는 용어로 그룹 멤버(서버)를 표현함

 

반동기 방식

  1. 한 서버에서 트랜잭션이 커밋 준비 완료 
  2. 그룹 멤버 간 합의
    • 트랜잭션 정보를 그룹의 다른 멤버들에게 전송
    • 과반수 이상의 멤버로부터 응답을 전달받으면 통과
    • 만약 과반수 이상의 멤버로부터 응답을 받지 못하면, 해당 트랜잭션은 그룹에 적용되지 않음
  3. 해당 트랜잭션을 인증 (충돌 검사)
    • 해당 트랜잭션이 이미 인증 단계를 통과한 선행 트랜잭션과 동시점에 동일한 데이터를 변경했는지 확인
  4. 최종적으로 커밋 처리를 완료함

 

2. 아키텍처

  • 별도의 플러그인으로 구현됨
  • 그룹 복제 플러그인 설치 필요

 

그룹 복제 플러그인
  • API를 통해 MySQL 서버로부터 이벤트를 전달받음 (서버 시작, 서버 복구, 트랜잭션 커밋 등)
  • 처리 중인 트랜잭션에 대한 커밋 또는 중단, 릴레이 로그 기록을 위한 요청 등을 MySQL 서버에 전달

 

복제 채널
  • group_replication_applier: 그룹에 실행된 모든 트랜잭션을 전달받아 적용함
  • group_replication_recovery: 그룹 분산 복구 작업 (그룹 간의 최신 데이터를 가지도록 함)

 

3. 모드

  • 프라이머리 서버 수에 따라 나뉨
  • group_replication_single_primary_mode 시스템 변수로 모드를 설정함
    • ON / OFF로 설정
    • 그룹 복제에 참여하는 서버들은 같은 값으로 설정되어야 함

 

싱글 프라이머리 모드

  • 그룹 내 프라이머리 서버가 단 한대만 존재
  • 그룹 복제 구축을 진행한 서버가 프라이머리 서버가 됨

 

프라이머리 서버가 변경될 경우
  • 그룹의 특정 멤버를 새로운 프라이머리로 지정한 경우 (group_replication_set_as_primary())
  • 현재 프라이머리 서버가 그룹을 탈퇴하는 경우

 

프라이머리 서버 지정 우선순위
  1. MySQL 서버 버전
    • 8.0.17 이상인 경우, 높은 버전이 우선순위 높음
    • 8.0.17 미만인 경우, 제외됨
  2. 각 멤버의 가중치 값
    • group_replication_member_weight 시스템 변수 값 비교
  3. UUID 값의 사전식 순서

 

프라이머리 서버 확인
select * from performance_schema.replication_group_members;

 

 

멀티 프라이머리 모드

  • 그룹 내 모든 노드가 프라이머리 서버로 동작하는 형태
  • 발생한 쓰기는 그룹의 다른 멤버들로 전파되어 각 멤버에서 다시 처리됨
  • 멤버간의 버전 호환성이 중요
    • 버전이 다르면 다른 멤버가 가지고 있지 않은 기능을 사용할 수 있으므로 동작이 안될수도 있음
    • 새로운 멤버가 추가될 때, 새로운 멤버의 버전이 그룹과 호환되는지 체크함

 

3. 그룹 멤버 관리

  • 그룹 멤버들에 대한 목록과 상태 정보를 내부적으로 관리함

 

 

그룹 멤버 확인

select * from performance_schema.replication_group_members;
그룹 멤버들의 정보 테이블
  • 호스트명, 사용하는 포트, UUID 값, MySQL 버전, 역할
  • View ID
    • 특정 시점의 그룹 멤버 목록 및 상태 정보를 의미
    • 그룹 멤버가 변경될 때마다 새로운 View ID가 생성됨
    • [Prefix Value]:[Sequence Value]
      • Prefix Value: 그룹 복제가 초기화될 때 생성됨 (타임스탬프 기반)
      • Sequence Value: 그룹에서 멤버가 변경될 때마다 1씩 증가하는 정수값
  • 상태
    • ONLINE, OFFLINE, ERROR, UNREACHABLE
    • RECOVERING: 그룹 복제에 참여하기 위해 복구 작업을 수행하는 중 (기존 그룹 멤버로부터 데이터를 전달받기)

 

자동 갱신
  • 탈퇴하거나 추가되면, 자동으로 테이블에 그룹 멤버 정보를 갱신함

 

4. 트랜잭션 처리

1. 협의

  • 그룹 멤버들에게 트랜잭션 적용을 제안하고 승낙을 받는 과정

 

과정 
  1. 클라이언트가 한 그룹 멤버에게 트랜잭션 실행 및 커밋 요청을 보냄
  2. 트랜잭션 데이터를 다른 멤버들에게 전파함 (그룹 통신 엔진(XCom)을 통해 전파)
    • 트랜잭션에서 변경한 데이터에 대한 WriteSet
    • 트랜잭션이 커밋될 당시의 gtid_executed 스냅숏 정보
    • 트랜잭션의 이벤트 로그 데이터
  3. 그룹 멤버들 간의 합의를 수행함 (Paxos 기반의 프로토콜을 바탕으로)
  4. 그룹의 과반수 이상에게 응답 메시지를 전달받으면, 해당 멤버는 다음 프로세스를 진행함
    • 과반수 이상에게 응답을 받지 못하면 트랜잭션은 적용되지 않으며, 클라이언트에게 에러가 응답됨

 

목적
  • 그룹 내 일관된 트랜잭션 적용이 목적

 

2. 인증

  • 협의를 통해 올라온 트랜잭션이 반영되기 전, 트랜잭션 충돌 여부를 확인하는 과정

 

과정
  1. 합의 단계를 거친 후, 멤버들은 정렬됨
  2. 각 멤버들은 트랜잭션 충돌 여부를 확인함
    • 해당 트랜잭션이 이미 인증 단계를 거친 선행 트랜잭션과 동시점에 동일한 데이터를 변경한 것인지를 검사
    • 전달받은 트랜잭션 WriteSet 데이터와 내부 데이터인 WriteSet 히스토리 데이터를 대조함

 

특징
  • 각 멤버들에서 모두 동일한 순서로 인증 단계를 거침
  • 멀티 프라이머리 모드에서만 발생
    • 그룹 멤버 전체가 쓰기를 처리할 수 있기 때문

 

3. 기록

요청 받은 멤버
  1. 바이너리 로그에 트랜잭션을 기록
  2. 트랜잭션 실행 (커밋)
  3. 클라이언트에 응답

 

그룹의 다른 멤버들
  1. 릴레이 로그에 기록 (어플라이어 스레드)
  2. 바이너리 로그에 기록
  3. 트랜잭션 실행

 

트랜잭션 일관성 수준

  • 멤버 간 데이터 일관성을 보장하는 방법

 

목적
  • 그룹 복제에서 각 멤버들은 모두 동일한 트랜잭션을 적용함
  • 그러나 일관성이 깨질 가능성이 있음
    • 실제 적용 시점까지 완전히 일치하지는 않음
    • 한 멤버가 쓰기를 수행한 후 바로 다른 멤버에서 해당 데이터를 읽었을 때, 최신 변경 사항이 반영되지 않았을 수도 있음
  • 트랜잭션 일관성 수준은 이 같은 상황을 방지할 수 있는 방법

 

group_replication_consistency
  • 트랜잭션 일관성 수준을 지정할 수 있음
  • EVENTUAL
    • 트랜잭션이 커밋되면 복제 그룹 내에서 순차적으로 전파됨
  • BEFORE_ON_PRIMARY_FAILOVER
    • 싱글 프라이머리 모드에서 적용됨
    • 프라이머리가 변경될 때, 새로운 Primary는 모든 트랜잭션을 적용한 후 승격됨
  • BEFORE
    • 요청받은 멤버가 커밋을 완료한 후에야, 다른 멤버에서 읽기가 가능함 (항상 최신 데이터만 읽음)
    • wait_timeout 까지 대기
  • AFTER
    • 요청받은 멤버의 커밋은 모든 멤버가 트랜잭션을 확정된 후에야 완료됨
  • BEFORE_AND_AFTER
    • 모든 선행 트랜잭션이 적용될 때까지 기다린 후 트랜잭션이 실행됨

 

흐름 제어

  • 그룹 멤버간의 트랜잭션 적용 불균형으로 인해 발생하는 문제를 방지
    • 특정 멤버가 다른 멤버들보다 스펙이 더 낮거나 대역폭이 적을 경우 트랜잭션 지연이 일어날 수 있음
    • 지연된 멤버가 트랜잭션을 실행할 때, 해당 트랜잭션은 최신 데이터가 아닌 오래된 데이터를 읽을 수 있음 (충돌 위험)
  • 그룹 멤버들의 쓰기 처리량을 조절하는 매커니즘이 구현되어 있음
    • 멤버 간 트랜잭션 갭을 적게 유지해서 멤버들의 처리량이 균등하도록 함
    • 멤버들의 데이터가 최대한 동기화된 상태가 유지됨

 

시스템 변수
  • group_replication_flow_control_mode
    • 흐름 제어 기능의 사용 여부 결정
    • QUOTA
      • 쓰기 멤버가 정해진 할당량만큼만 쓰기를 하도록 제어
      • 네트워크 및 성능이 낮은 멤버를 기준으로 조정하여 모든 멤버가 동기화된 상태를 유지함
    • DISABLED
      • 흐름제어 비활성화
      • 느린 멤버가 뒤처지더라도 다른 멤버들의 흐름제어에 영향을 미치지 않음
  • group_replication_flow_control_period
    • 흐름 제어가 동작되는 주기 (ms)
    • 지정된 시간마다 통계 정보가 수집됨
  • group_replication_flow_certifier_threshold
    • 인증 단계에서 인증 큐에 대기중인 트랜잭션 수가 지정된 수를 초과하면 흐름 제어 발동
  • group_replication_flow_applier_threshold
    • 적용 단계에서 어플라이어 큐에서 대기중인 트랜잭션 수가 지정된 수를 초과하면 흐름 제어 발동
  • group_replication_flow_control_min_quota
    • 모든 멤버에게 보장해야 할 최소 쓰기 처리량
  • group_replication_flow_control_min_recovery_quota
    • 복구 중인 멤버에게 보장해야 할 최소 쓰기 처리량
  • group_replication_flow_control_member_quota_percent
    • 할당된 쓰기 처리량 중 실제 사용 가능 비율
  • group_replication_flow_control_hold_percent
    • 할당량 중에서 사용하지 않고 남겨둘 비율
  • group_replication_flow_control_release_percent
    • 흐름 제어가 필요 없을 때, 주기당 증가시킬 할당량의 비율
  • group_replication_flow_control_max_quota
    • 멤버가 최대 사용할 수 있는 쓰기 처리량 설정

 

쓰기 처리량 계산 로직
  1. 통계 정보로 계산된 쓰기량과 group_replication_flow_control_min_quota 중 작은 값을 선택
    • 복구중인 멤버는 group_replication_flow_control_min_recovery_quota 사용
  2. 1에서 계산된 값에서 group_replication_flow_control_member_quota_percent 값 적용
  3. group_replication_flow_control_max_quota 값이 설정되어 있을 경우 2와 비교하여 더 작은 값을 선택함

 

5. 자동 장애 감지 및 대응

장애 감지 및 멤버 추방

  1. 멤버 간 상태 확인
    1. 주기적으로 통신 메시지를 주고받으며 서로의 상태 확인
    2. 5초 내 응답이 ㅇ벗으면 문제 발생 가능성이 있다 판단함
  2. 의심 멤버 감지
    1. 응답이 없는 멤버를 의심 상태로 설정 (UNREACHABLE)
    2. 과반수의 멤버가 동의하면 해당 멤버는 그룹에서 추방됨
  3. 멤버 추방 및 새로운 VIEW ID 부여
    1. 추방된 멤버를 그룹에서 제외하고 새로운 VIEW ID 부여
    2. 멤버 간의 그룹 구성이 변경됨

 

시스템 변수
  • group_replication_member_expel_timeout
    • 추방 전 대기시간 (초)
  • group_replication_unreachable_majority_timeout
    • 소수의 멤버들이 과반수의 멤버들과 통신이 단절됐을 때, 일정 시간 동안 대기한 후 스스로 탈퇴하도록 설정 (네트워크 이슈)

 

자동 재가입 기능

  • 추방된 멤버가 그룹에 다시 합류하려면 재가입 시도를 해야 함
  • 재가입이 설정되지 않은 경우, 탈퇴된 서버는 종료 액션이 실행됨

 

재가입 시도 정보 확인
select * from performance_schema.events_stages_current;

 

시스템 변수
  • group_replication_autorejoin_tries
    • 추방된 멤버가 그룹에 재가입을 시도하는 횟수
    • 5분 간격으로 시도
  • group_replication_exist_state_action
    • ABORT_SERVER: 탈퇴된 서버를 종료시킴 (기본값)
    • READ_ONLY
    • OFFLINE_MODE

 

6. 분산 복구

  • 그룹 복제에서 탈퇴된 멤버가 다시 가입할 떄 기존 멤버와 데이터를 동기화하는 과정

 

필요성

  • 탈퇴된 멤버가 다시 그룹에 합류할 때, 그룹에서 발생한 트랜잭션을 모두 적용해야 데이터 일관성이 유지됨
  • 이전 탈퇴 시점 이후에 반영되지 않은 트랜잭션을 복구해야 정상적으로 동작이 가능함
  • 이때, 기존 그룹 멤버 중 한 명을 기증자 멤버로 지정하여 복구를 수행함

 

분산 복구 방식

바이너리 로그 복제 방식
  • 특징
    • 비동기 복제 기반
    • 가입 멤버는 group_replication_applier 복제 채널을 통해 기증자 멤버의 바이너리 로그를 복제
    • 가입 멤버에 적용되지 않은 트랜잭션을 가져와 순차적으로 실행
  • 작동 방식
    1. 가입 멤버가 기존 그룹 멤버의 바이너리 로그에서 미적용 트랜잭션을 찾음
    2. 발견된 트랜잭션을 가입 멤버에 적용하여 데이터 동기화
    3. 가입 완료 후, 그룹 복제에 정상적으로 참여
  • 시스템 변수
    • group_replication_recovery_retry_count
      • 바이너리 로그 복제 방식으로 복구 작업 진행 시, 가입한 멤버가 기존 그룹 멤버에 연결을 시도하는 횟수
    • group_replication_recovery_reconnect_interval
      • 기존 그룹 멤버들에 대한 연결 시도 사이의 대기 시간 지정

 

원격 클론 방식
  • 특징
    • 클론 플러그인 사용
    • 모든 멤버에 클론 플러그인이 설치되어 있어야 함
  • 작동 방식 
    1. 기존 그룹 멤버의 InnoDB 스토리지 엔진 데이터를 일관된 스냅샷으로 가져와 복구
    2. 가입한 멤버의 기존 데이터는 모두 삭제되고, 기증자 멤버의 스냅샷 데이터로 대체됨
    3. 클론 후에도 바이너리 로그 복제 방식으로 추가 복구 진행
  • 사용되는 경우
    • 가입한 멤버와 기존 멤버 간의 트랜잭션 갭이 큰 경우
    • 필요한 트랜잭션이 기존 그룹 멤버의 바이너리 로그에서 삭제된 경우
  • 시스템 변수
    • group_replication_clone_threshold
      • 원격 클론 방식을 복구 방식으로 채택하게 되는 트랜잭션 갭의 임곗값
    • group_replication_start_on_boot
      • 원격 클론 방식에서 기증자 멤버의 스냅숏 데이터를 모두 전송받고 재부팅 후 바이너리 로그 복제를 자동으로 실행할 지 여부
      • OFF일 경우, 수동으로 명령을 실행해야 함 (START GROUP_REPLICATION)

 

프로세스

  1. 로컬 복구
    • 뷰 변경 로그 이벤트 생성
      • 새로운 멤버가 그룹에 가입할 경우 발생
      • 그룹 뷰가 변경되어 VIEW ID가 새로 발급됨
      • 멤버들의 바이너리 로그에 해당 이벤트가 기록됨
    • 가입한 멤버가 이전에 가입한 적이 있는 경우 릴레이 로그에 미처 적용하지 못한 트랜잭션이 있을 수 있음
    • 이 트랜잭션들을 먼저 적용한 후 본격적인 작업을 진행함
  2. 글로벌 복구
    • 가입 멤버는 그룹의 기존 멤버들에서 기증자 역할을 할 멤버를 선택
    • 기증자 멤버로부터 데이터 또는 누락된 트랜잭션들을 가져와 자신에게 적용함
    • 복구 작업 동안, 현재 그룹에서 처리되는 트랜잭션들을 내부적으로 캐싱해둠
  3. 캐시 트랜잭션 적용
    • 글로벅 복구 단계가 완료되면 캐싱에서 보관하고 있던 트랜잭션들을 적용해 최종적으로 그룹에 참여함

 

설정

group_replication_recovery_complete_at
  • TRANSACTIONS_CERTIFIED: 누락된 트랜잭션들이 인증 단계까지 완료했을 때 ONLINE으로 변경
  • TRANSACTIONS_APPLIED: 누락된 트랜잭션들이 모두 적용되어 기록됐을 때 ONLINE으로 변경

 

오류 처리

  • 복구 작업 중 오류가 발생할 경우 자동으로 다시 작업을 시도함
  • 새로운 그룹 멤버로 연결을 전환함

 

바이너리 로그 복제 분산 복구 에러 확인 테이블
  • performance_schema.replication_connection_status
  • performance_schema.replication_applier_status_by_worker

 

원격 클론 방식 분산 복구 에러 확인 테이블
  • performance_schema.clone_progress
  • performance_schema.clone_status

 

7. 요구 사항

InnoDB 스토리지 사용
  • 충돌 감지 시 트랜잭션 롤백 지원이 필요함
  • disabled_storage_engines에 트랜잭션 롤백을 지원하지 않는 스토리지 엔진을 기입하여 에러를 방지함

 

프라이머리 키 사용
  • 그룹에서 복제될 모든 테이블들은 프라이머리 키를 가지고 있어야 함
  • 이를 바탕으로 트랜잭션 간의 충돌을 감지함

 

원활한 네트워크 환경
  • 그룹 멤버들 간의 양방향 통신을 통해 이루어짐
  • 정상적으로 통신이 이루어져야 원활하게 사용 가능

 

바이너리 로그 활성화
ROW 포맷 형태의 바이너리 로그 사용
log_slave_updates 활성화
  • 레플리카 서버에서 받은 트랜잭션을 다시 바이너리 로그에 기록할지 설정
    • 기본적으로 레플리카 서버는 자신에게 들어온 트랜잭션을 바이너리 로그에 기록하지 않음
  • 새로운 멤버가 그룹에 참여하면, 기존 멤버들과 동일한 데이터를 가질 수 있도록 분산 복구 작업 수행
    • 기존 그룹 멤버의 바이너리 로그를 복제해서 자신에게 적용
    • 그룹 멤버들은 그룹에서 발생한 트랜잭션들을 모두 각자의 바이너리 로그에 기록해야 함

 

GTID 사용
  • 기본적으로 GTID를 사용함

 

고유한 server_id 사용
복제 메타데이터 저장소 설정
  • 복제 관련 메타데이터는 파일이 아닌 테이블에 저장되어야 함
  • master_info_repository 및 relay_log_info_repository 값이 TABLE로 되어있어야 함

 

WriteSet 설정
  • 트랜잭션에서 변경한 데이터 정보
  • transaction_write_set_extraction 변수가 XXHASH64로 설정되어야 함

 

테이블 스페이스 암호화 설정
  • default_table_encryption 값이 모든 그룹 멤버에 동일하게 설정되어야 함

 

lower_case_table_names 설정
  • lower_case_table_names 값이 모든 그룹 멤버에 동일하게 설정되어야 함

 

멀티 스레드 복제 설정
  • 멀티 스레드를 사용하여 트랜잭션을 병렬 처리할 수 있음
  • slave_preserve_commit_order
    • 복제 트랜잭션이 멀티 스레드로 적용될 때 원본 서버에서 커밋된 순서와 동일한 순서로 커밋되도록 해야 함
    • ON으로 설정하기