단일 리더 복제와 장애복구

Alan·2023년 4월 1일
0

복제

  • 네트워크로 연결된 여러 장비에 동일한 데이터의 복사본을 유지하는 것

  • 장점

    • 지리적으로 사용자와 가깝게 데이터를 유지해 지연시간을 줄일 수 있음

    • 장애 발생시, Fail-over 가능

    • Read-only 장비를 확장해 읽기 처리량을 늘릴 수 있음

  • 데이터가 변경되지 않는다면, 복제를 구현하는 것은 어렵지 않음

    • 모든 노드에 데이터를 복사하면 되기 때문
  • 복제된 데이터의 변경처리가 복제에서 어려운 부분이며, 이를 위한 대표적인 알고리즘은 다음과 같음

  • 단일 리더 복제

  • 다중 리더 복제

  • 리더 없는 복제

리더와 팔로워

  • 데이터베이스의 복사본을 저장하는 노드를 복제 서버(replica)라고 함

  • 데이터베이스의 모든 쓰기는 모든 복제 서버에서 처리되어야 함

    • 이를 위해 리더 기반 복제(active/passive, master-slave)을 사용
  • 클라이언트의 쓰기 요청은 Master에게 보내져야 하며, Master는 자신의 로컬에 새로운 데이터를 저장함

  • slave는 master가 보내는 복제 로그(replication log)나 변경 스트림(change stream)을 받아서, 모든 쓰기를 동일한 순서로 적용하여 데이터베이스를 복사함

  • 즉, 쓰기는 모두 Master에서만 이루어지며, 읽기는 어느 곳에서도 일어날 수 있다는 전제 조건을 토대로 Master의 데이터를 복사하는 방식임

  • 이러한 방식을 사용하는 DB는 MySQL, Oracle, MongoDB, Kafka 등이 있음

동기식 대 비동기식 복제

  • 동기식(팔로워1)

    • 리더는 팔로워1이 쓰기를 수신하는 것을 기다리며, 확인 후 성공을 보고

    • 이는 팔로워가 항상 최신 데이터의 복사본을 보유하고 있음을 보장

    • 하지만, 팔로워가 응답하지 않는 상황이라면 쓰기 처리가 되지 못하는 단점이 존재

    • 데이터베이스에서 동기식 복제를 사용한다는 것은 하나의 팔로워만 동기식으로 구성하고 그 밖에는 비동기식으로 하는 것을 의미함(반동기식이라고도 함)

  • 비동기식(팔로워2)

    • 리더는 팔로워2가 메시지를 처리하는 것과 관계없이 자신의 처리만 완료되면 성공을 보고

    • 이는 팔로워가 잘못되더라도 쓰기 처리에 문제가 없다는 것을 의미

    • 하지만 팔로워가 항상 최신 데이터의 복사본을 보유하고 있다고 보장할 수 없음

    • 보통 리더 기반 복제는 완전히 비동기식으로 구성

    • 이는 리더가 잘못되고 fail-over될 수 없다면, 팔로워는 아직 복제하지 않은 모든 쓰기를 유실할 수 있다는 의미

  • 복제의 발전

    • 마이크로소프트 Azure 저장소는 조금 더 발전된 형태의 체인 복제를 사용(동기식 복제의 변형)

    • 이번 장에서는 가장 일반적이고 간단한 복제의 형태에 대해 알아봄

    새로운 팔로워

    • 새로운 팔로워를 설정한다면 리더의 데이터 복제본을 어떻게 복사해야 할까?

    • 단순히 한 노드에서 새로운 노드로 데이터 파일을 복사하는 것으로는 충분치 않음

      • 데이터는 계속해서 기록되고, 데이터는 유동적으로 변할 수 있기 때문에 시점에 따라 데이터가 변할 가능성이 존재하기 때문

      • 데이터베이스를 잠가서(Read-only) 파일의 일관성을 보장할 수는 있지만, 고가용성의 목표에 부합되지 않음

    • 과정

      • 데이터베이스를 잠그지 않고 리더의 DB 스냅샷을 일정 시점에 가져옴

      • 스냅샷을 새로운 팔로워에 복사

      • 팔로워는 리더에 연결해 스냅샷 이후 발생한 모든 데이터 변경을 요청(postgresql에서는 log sequence number라고 하고, MySQL에서는 binlog coordinate라고 함)

      • 팔로워 스냅샷 이후 발생한 데이터 변경의 미처리분(backlog)를 모두 처리하면 복제가 완료

단일 리더 복제의 failover

  • 리더 장애 복구

    • 팔로워 중 하나를 리더로 승격하고, 클라이언트의 쓰기 요청을 새로운 리더에게 전송하도록 설정하고, 다른 팔로워들은 새로운 리더로부터 데이터 변경을 소비하도록 변경하는 과정을 장애복구(failover)라고 할 수 있음
  • 장애 복구 순서

    • 리더에 장애가 발생했는지 판단. 이유는 여러 가지가 있을 수 있으며 무엇을 장애라고 판단해야 하는지에 대한 기준이 애매하기 때문에 보통 time-out으로 장애를 판단함

    • 새로운 리더를 선출. 선출과정을 통해 이루어지거나 제어 노드(controller node)를 통해 새로운 리더가 임명

    • 새로운 리더를 사용하기 위한 시스템 재설정. 클라이언트가 새로운 리더에게 쓰기 요청을 할 수 있도록 재설정함

  • 장애 복구시 나타날 수 있는 문제

    • 비동기식 복제를 사용할 경우, 리더가 실패하기 전의 일부 쓰기에 대해 수신하지 못할 수 있음. 이를 위한 가장 일반적인 해결책은 이전 리더의 복제되지 않은 쓰기를 단순히 폐기하는 것

    • 쓰기 폐기의 경우, 데이터베이스 외부의 다른 시스템이 데이터베이스 내용에 맞춰 재조정되어야 한다면 특히 위험. 깃허브 사례 중, 새로운 로우에 기본키를 할당하기 위해 auto-incresement를 사용했지만, failover에서 새로운 리더가 승격되었고, 새로운 리더는 이전의 기본키를 가지고 있지 않았기 때문에 외부 시스템에서 이전에 할당된 키본키를 사용하는 문제가 발생. 결과적으로 일부 개인 데이터가 잘못된 사용자에게 제공되는 오류로 이어짐

    • 특정 결함 시나리오에서 두 개의 노드가 모두 자신이 리더라고 생각할 수 있음(split-brain). 이와 같은 상황에서는 두 리더가 쓰기 요청을 받으면서 데이터가 유실되거나 오염될 수 있음

복제 로그 구현

  • 구문 기반 복제

    • 모든 쓰기 요청(statement)를 모두 기록하고 쓰기를 실행한 다음 구문 로그를 팔로워에 전송하는 방식(INSERT, UPDATE, DELETE 등)

    • 팔로워는 모든 구문을 실행하여 데이터를 복제

    • 문제점

      • NOW(), RAND() 등의 비결정적 함수를 호출하는 모든 구문은 각 복제 서버마다 다른 값을 가지게 함

      • Auto-incresement 등의 구문은 각 복제 서버에서 정확히 같은 순서로 실행되어야 함. 즉, 이러한 방식은 동시에 여러 트랜잭션이 수행되는 것을 제한하게 할 수 있음

    • 해결

      • 모든 비결정적 함수 호출을 고정 값을 반환하도록 대체

      • 하지만 여러 에지 케이스가 있을 수 있기 때문에 일반적으로 구문 기반 복제보다 다른 방법을 선호함(MySQL 5.1 이전 버전에서 구문 기반 복제를 사용함)

  • 쓰기 전 로그 배송

    • 로그 구조화 저장소 엔진(LSM Tree)나 B-Tree 모두 로그는 데이터베이스의 모든 쓰기를 포함하는 append-only 바이트 열임

    • 팔로워는 이 로그를 사용해 리더와 정확히 동일한 데이터를 구축

    • 단점

      • 로그가 저수준의 데이터를 기록한다는 것(어떤 디스크 블록에서 어떤 바이트를 변경했는지와 같은 상세 정보들)

      • 즉, 복제가 저장소 엔진에 dependency로 엮일 수 있다는 의미

      • 다시 말해, 데이터베이스가 저장소 형식을 다른 버전으로 변경한다면, 리더와 팔로워의 데이터베이스 소프트웨어 버전을 다르게 실행할 수 없다는 의미

      • 별 것 아닌 것 같지만, 팔로워가 리더보다 새로운 소프트웨어 버전을 사용할 수 있다는 것은 업그레이드시, 팔로워의 버전을 업그레이드 함으로써 중단시간 없이 데이터베이스의 소프트웨어 버전을 업그레이드할 수 있다는 의미

  • 논리적(로우 기반)로그 복제

    • 위 쓰기 전 로그 배송 방식을 보완하기 위한 방법

    • 복제와 저장소 엔진을 분리하기 위해 복제 로그를 저장소 엔진의(물리적) 데이터 표현과 다른 새로운 형식(논리적 로그)로 표현하는 것

    • 보통 RDBMS의 논리적 로그는 로우 단위로 데이터베이스 테이블에 쓰기를 기술한 레코드 열임

      • 삽입된 로우의 로그는 모든 칼럼의 새로운 값을 포함

      • 삭제된 로우의 로그는 고유 식별자를 포함

      • 갱신된 로우의 로그는 고유 식별자와 모든 칼럼의 새로운 값을 포

    • MySQL에서 사용하는 방식

    • 논리적 로그는 외부 애플리케이션이 파싱하기 쉬운 측면을 가지고 있기 때문에 사용자 정의 색인, 캐시 구축을 위한 데이터 웨어하우스 같은 외부 시스템에 데이터베이스의 내용을 전송하고자 할 때 유용. 이를 CDC(change data capture)라고 하며 11장에서 알아봄

  • 트리거 기반 복제

    • 위 복제 방식과는 달리 애플리케이션 코드의 사용을 통한 복제 방식

    • 유연성이 필요한 상황에 사용. ex) 다른 데이터베이스로 데이터를 복제해야하거나 충돌해소로직이 필요

    • 트리거는 사용자 정의 애플리케이션 코드를 등록할 수 있도록 하고, 데이터베이스 시스템에서 데이터가 변경되면(쓰기 트랜잭션) 자동으로 실행될 수 있게 함

    • 다른 복제방식들보다 많은 오버헤드가 존재하며, 데이터베이스에 내장된 복제보다 버그나 제한 사항이 더 많이 발생할 수 있음

0개의 댓글