본문 바로가기
Database

리플리케이션(Replication)

by !!e!! 2021. 10. 21.

얼마전 회사에서 개발을 완료하고 QA 환경에서 확인하던 중 분명 있어야할 데이터가 없다는 에러로그를 확인했다. 그렇게 문제를 따라가보니 이게 왠걸 라이브 쪽 데이터와 같아야할 리플리케이션(Replication)된 DB가 깨져버렸다. 다행히 인프라팀에 요청해서 해결할 수 있었지만, 개발자로서 왜 이런 문제가 발생했고 또 리플리케이션이 정확히 뭔지 짚고 넘어가야 할것 같아서 내용을 정리해보기로 했다.

리플리케이션 (Replication)

개인적으로 재미있게 본 영화 [블레이드 러너 2049] 를 보면 미래의 인류가 인간의 노동력을 대체하기 위해 만들어진 복제인간, 리플리칸트(Replicant)가 등장한다. 리플리칸트는 비서, 경찰, 군인 등의 특수한 목적을 달성하고자 인간(Master)의 명령에 따르는 복제된 존재(Slave)이다.

데이터베이스에서 리플리케이션(Replication)도 리플리칸트와 같이 특수한 목적을 달성하기 위해 데이터를 복제하여 Master-Slave 구조로 구성하는것을 말한다. 그렇다면 리플리케이션을 통해 달성하고자하는 그 특수한 목적은 무엇일까?

잘 생각해보면 복제(Replicate) 라는 행위 자체가 Scale-Out 솔루션이다. 영화 [블레이드 러너 2049] 에서도 미래의 인류는 한 인간이 영생을 누리고 능력이 출중해지는 Scale-Up 을 선택하기 보다 무수한 복제본을 만들어 여러 문제를 해결하는 Scale-Out 방식을 택했다.



리플리케이션이 필요한 이유

1. 저지연성

서비스에서 트래픽이 많은 경우 DB 에도 자연스럽게 트래픽이 늘어나게 된다. 이때 데이터의 성격에 따라 캐시를 활용하는 방법도 있지만, 캐시로 해결되지 않는 경우도 많이 발생하게 된다. 어쩔수 없이 DB 접근이 많아지는 경우 DB 요청의 70~90%가 읽기 작업인 특성을 활용해 복제된 Slave를 조회용으로 활용하고, 변경이나 추가가 있는 경우 Master로 수행되도록 함으로써 트래픽 분산을 통해 저지연성을 달성할 수 있다.

2. 사용자 접근성

유튜브와 같은 글로벌 서비스의 경우 국가, 지역 상관없이 유사한 퀄리티의 서비스가 제공되어야 한다. 같은 페이지인데 한국에서 접근하는 경우와 미국에서 접근하는 경우에 로딩시간이 크게 차이가 나면 안될것이다. 고도화된 캐싱전략을 통해서도 이 문제를 해결하겠지만, 리플리케이션을 통해 각 국가, 지역 별로 DB를 배치 시킴으로서 사용자에게 보다 용이한 접근성을 제공하게 된다.

3. 데이터 안정성

투자 격언 중에 계란을 한 바구니에 담지 말라 는 말이 있다. DB 역시 한 곳에 집중된다면, 예기치 못한 장애나 지진, 화재 같은 것에 의해 복구할 수 없는 큰 피해를 입게될 수 있다. 이 경우에도 리플리케이션을 통해 물리적으로 다른 공간에 위치 시킨다면 추후 문제가 발생했을때 장애를 복구하거나 피해를 최소화할 수 있게 된다.


리플리케이션의 종류

동기 방식 (클러스터링)

동기방식은 클러스터링(Clustering)이라고 표현하기도 한다. 위의 그림처럼 메인 DB의 트랜잭션을 수행한 세션으로 반환하기 전 복제 DB에 트랜잭션 커밋을 요청하고 모든 노드들이 동기화 되면 그 결과를 반환하게 된다. 즉, 동기방식은 Master 노드에 데이터 변경이 발생한 경우 Slave 노드까지 적용되어 데이터 정합성을 보장하는 방식이다.

장점

  • Master 노드에 장애가 발생하더라도 데이터 정합성 문제 없이 항상 일관성 있는 데이터를 제공받아 서비스를 이어갈 수 있다
  • DB 서버 부하가 분산된다

단점

  • 여러 노드의 데이터를 동기화하는 시간 때문에 쓰기 성능이 떨어진다

  • 장애가 전파된 경우 처리하기가 까다롭고 하나의 노드에서만 실패하더라도 모두 실패하게 된다 (데이터 정합성을 유지하기위함)

동기화에도 2가지 종류가 있다. 모든 노드를 활성화 하여 서버 부하를 분산하는 Active-Active 방식과 대기서버를 두어 장애가 발생한 경우에만 Fail Over 하는 Active-Stanby 방식이 있다. 모든 서버에서 서비스를 제공하는 것이 아니라 Active 상태의 메인 서버 장애시 클러스터 Heart beat를 통해 Stanby 서버가 Fail Over 하는 방식으로 동작하게 된다.

비동기 방식

비동기 방식은 데이터 정합성이 아닌 저지연성에 중점을 둔 방식이다. 요청에 대한 결과를 반환후 비동기적으로 복제 DB와 싱크를 맞추게 된다.

장점

  • DB 요청의 70~90% 정도가 읽기 작업이라는 점을 활용하여 Master와 Slave를 조회, 추가및 수정 용도로 나누어 활용하게 된다면 충분히 리플리케이션의 장점들을 누릴 수 있다
  • 동기 방식과 다르게 지연시간이 거의 없다

단점

  • 노드들 간의 데이터 정합성이 보장되지 않아 일관성 있는 데이터 조회가 어려울 수 있다

  • Master와 Slave 간의 데이터 유실 및 장애가 생기더라도 파악하기 어려워 질수 있다

서비스 특징에 따라 다르지만 데이터 정합성보다 빠른 응답이 중요한 서비스들은 비동기 방식을 선호할 수 밖에 없지만, 데이터 유실에 대한 부분이 여전히 문제가 될수 있다. 그래서 동기와 비동기 방식의 장점을 섞은 반동기(Semi-Sync) 방식도 있다.

반동기 방식은 메인 DB로부터 최소 하나의 복제 DB가 이벤트를 수신하고 기록 할 때까지 기다린 다음(복제본 수는 조절 가능) 트랜잭션을 커밋한다. 즉, 메인 DB는 모든 복제 DB가 수신을 승인할때까지 기다리지 않고 복제 DB들 중 하나의 승인이라도 받으면 트랜잭션을 커밋하게 된다. 따라서 반동기 방식은 최소 하나 이상의 완벽한 복제본을 보장하는 방식으로 비동기 방식에 비해 보다 향상된 데이터 무결성(데이터의 정확성과 일관성을 유지)을 제공하면서도 동기 방식보다는 커밋 속도가 더 빨라지게 된다.


비동기 Replication 동작 원리 (MySQL)

MySQL의 경우 기본적으로 비동기 복제 방식을 사용하고 있다. Master 노드에서 변경되는 데이터에 대한 바이너리 로그를 기록하면 Master 스레드가 비동기적으로 Slave 쪽으로 전송하는 방식이다.

  1. 클라이언트에서 커밋을 수행
  2. 접속스레드는 스토리지 엔진에게 해당 트랙잭션에 대한 커밋 준비
  3. 커밋을 수행하기 전 바이너리 로그에 변경사항을 기록
  4. 커밋 수행
  5. Master 스레드는 비동기적으로 바이너리 로그를 읽어 Slave로 전송
  6. Slave의 I/O 스레드는 Master로부터 수신한 데이터를 릴레이 로그에 기록
  7. Slave의 SQL 스레드는 릴레이 로그에 기록된 변경 데이터를 읽어서 Slave 스토리지 엔진에 적용

    SBR(Statement Based Replication) vs RBR(Row Based Replication)

앞에서 바이너리 로그로만 표현했지만 실제로 데이터를 다른 노드로 복제하는 상황에서는 SQL을 전송하여 Replay 하는 방식으로 복제할 것인지, 변경되는 Row 데이터를 전송하여 복제할 것인지 결정해야한다.

  • SBR(Statement Based Replication) : SQL을 전송하여 Replay하는 방식으로 로그의 크기가 작아서 저장공간 확보에 유리하고 작업을 빠르게 수행할 수 있다. 하지만 비결정적 동작(Unsafe Statements)은 복제하기 어렵다.
  • RBR(Row Based Replication) : 변경되는 Row 데이터를 전송하여 복제하는 방식으로 변경 사항을 안전하게 복제할 수 있다. 하지만 기록해야하는 데이터가 많아져서 더 많은 저장공간이 필요하고 복제하는데 더 오랜 시간이 걸린다.
  • MBR(Mixed Based Replication) : SBR과 RBR의 단점을 보완한 방식으로 평상시에는 SBR로 동작하다가 비결정적 동작을 만나면 RBR로 동작한다.

비결정적 동작(Unsafe Statements)이란 명령문의 결과가 항상 동일하지 않은 동작을 의미한다. 반대로 결정적 동작(Safe Statements)은 명령문이 항상 동일한 결과를 내는 것을 의미한다. (비결정 동작)



그렇다면 내가 겪었던 문제는..?

서두에서 회사의 QA 환경에서 리플리케이션된 DB에 문제가 있었다고 했다.

우선은 내가 담당하고 있는 서비스는 정기적인 외부 API 호출을 통해 데이터를 지속적으로 쌓게된다. 그런데 이 외부 API 호출이 라이브 서버에만 허용되고 QA에서의 호출을 외부에서 막아놓은 상황이다. (계약상의 이유였던것 같다) 그러다 보니 QA 과정에 필요한 실제 데이터를 확인하기위한 용도로 라이브 DB를 QA에 리플리케이션해서 사용하던 중이었다.

리플리케이션이 깨진것을 확인하기 며칠전, 라이브 DB에서 잘못입력된 데이터가 있어서 삭제하면서 AUTO_INCREMENT 를 다시 설정하는 일이 있었다. 앞서 SBR 방식에서는 비결정적 동작은 복제가 어렵다고 했었다. AUTO_INCREMENT 업데이트는 대표적인 비결정적 동작이라고 할 수 있다. 더 자세히 찾아보니 버그로 보고되고 있는데, SBR 방식에서 AUTO_INCREMENT 열을 업데이트하거나 트리거하는 함수를 실행하면 복제되지 않을 수 있다고 한다. (Replication and AUTO_INCREMENT)

이로써 내가 겪었던 문제에서 리플리케이션 방식이 SBR 이었다는것을 역으로 추론해볼 수 있었다. 내가 겪은것 이외에도 리플리케이션 중단을 유발하는 케이스들이 있는데 다음과 같다.

  • Schema 를 직접적으로 변경하게 되는 컬럼 추가
  • RBR 방식에서 컬럼 중간 삽입
  • 복제 지연

복제 지연은 리플리케이션을 사용하는 경우 발생하기 쉬운 부분인데, 다음 시간에 샤딩(Sharding)과 함께 정리해보도록 하겠다.


참고 링크

MySQL :: MySQL 5.7 Reference Manual :: 16 Replication

MySQL :: MySQL 5.7 Reference Manual :: 16.3.9 Semisynchronous Replication

MySQL :: MySQL 8.0 Reference Manual :: 17.2.1.1 Advantages and Disadvantages of Statement-Based and Row-Based Replication

System Design - Database Replication | Synchronous vs Asynchronous

MySQL - Replication 구조 - Rain.i

Clustering vs Replication vs Sharding

[Database] 리플리케이션(Replication) vs 클러스터링(Clustering)