Backend/DB

[DB] Replication에 대해 알아보자

Emil :) 2025. 2. 8. 18:40
728x90
반응형

서론


회사에서 대규모 업데이트를 진행함에 따라, 기존 MariaDB 10.2에서 MySQL 8.0으로 migration하는 작업을 진행했다.

이 과정에서 필연적으로 replication 작업도 진행했는데, 하는 방법도 중요하지만 원리에 대해 조금 더 알아보고자 한다.

본론


Replication?

Replication(복제)은 write 연산과 read 연산을 분산시켜 DB의 부하를 낮추고, 시스템 장애 발생 시 slave를 master DB로 승격시켜 빠른 장애 대응이 가능한 기법을 의미한다.

정의 같은 것은 너무 기본적인 내용이고 더 자세하게 소개한 레퍼런스가 많으니 패스하고, 간단하게 도식화한 그림으로 이해해보자.

어떤 원리로 작동하는가?

크게 동기식, 비동기식, 반동기식(semi-sync)가 존재한다.

반동기식은 간단한 개념이기에 동기식을 설명하면서 같이 설명하도록 하겠다.

참고로 MySQL은 비동기식을 기반으로 replication이 동작한다.

동기식

리더 기반 복제라고도 하며, 리더 노드가 하위 노드들의 연산이 정상적으로 종료될 때까지 기다리는 방식을 의미한다.

 

장점

최고 수준의 무결성과 정합성을 보장한다.

모든 복제본에 즉시 적용되므로 트랜잭션 안정성이 보장된다.

 

단점

하위 노드의 연산이 종료될 때까지 기다려야 하기에 시간이 오래걸린다.

하나의 노드가 실패하면 전체 시스템이 동기화를 기다려야 하기에 서비스 지연 발생의 가능성이 존재한다.

서브 노드들이 많아질수록 네트워크 부하가 심해진다.

 

FM은 서브 노드들로부터 모두 ok 응답을 받아야 비로소 사용자에게 ok를 return해주는 것이지만, 현실적으로 시간이 너무 오래걸려 사용자 경험을 해칠 수 있다. 따라서 서브 노드 하나는 동기식, 나머지는 비동기식으로 운용한다. (이를 반동기식이라고 일컫는다.)

그림 내 sub node 2의 점선으로 표시된 부분이 이를 의미한다.

비동기식

출처 : http://cloudrain21.com/mysql-replication

bin log(바이너리 로그)와 relay log 등을 사용해 특정 노드에서 처리된 쿼리를 다른 노드에서 처리하는 방식이다.

(직접 그리려다가 너무 잘 표현된 그림이라 그냥 가져왔다.)

 

장점

비동기식으로 처리되어 빠른 속도를 보장해준다.

서버는 master, slave DB와 개별적으로 통신하기 때문에 네트워크 부하가 감소한다.

 

단점

DB 간의 정합성이 보장되지 않는다.

복구 시 최신 데이터의 보장이 되지 않는다.

 

해당 내용이 본 포스팅의 핵심이니 조금 깊게 알아보자. 

순서는 다음과 같다. 주요 포인트들에 대해서도 알아보자.

1. 클라이언트로부터 Commit이 수행된다.
2. Commit 이전에, 스토리지 엔진에 트랜잭션에 대한 Prepare 상태로 전환하라는 명령을 스토리지 엔진에 내린다.
3. 이와 동시에, db의 변동사항 (CRUD)과 관련된 쿼리를 바이너리 로그로 남긴다.
4. 스토리지 엔진에게 트랜잭션 commit을 수행한다.
5. 마스터 스레드에게 바이너리 로그를 기반으로 replication을 실행해달라는 명령을 하달한다.
6. 슬레이브의 I/O 스레드에서 변경 데이터를 릴레이 로그 (바이너리 로그와 이름만 다르지 역할은 똑같다.)에 기록한다.
7. 변경된 SQL을 수행할 슬레이브의 SQL 스레드에서 SQL문을 실행하고, 스토리지 엔진에 적용한다.

 

1. Log format

3번 단계에서, 바이너리 로그로 복원 데이터를 어떻게 보낼지는 두 가지 방법이 있다.

 

SBR(Statement Based Replication)

SQL문을 전송해 쿼리를 다시 실행시키는 방식

 

RBR(Row Based Replication)

row data를 그대로 전송하여 복제하는 방식

 

MySQL은 SBR을 기본 방식으로 채택하고 있다. 단, GTID 기반 replication를 채택한 경우 트랜잭션 부하 때문에 RBR을 권장한다.

GTID도 매우 중요한 개념 중 하나인데, 이에 대해서는 해당 링크를 통해 공부해보도록 하자.

간단히 말하자면 글로벌 트랜잭션 식별자로, 트랜잭션들이 가지는 PK 값이라고 이해하면 된다.

 

2. Master thread와 Slave thread의 관계

Master thread는 Slave thread와 1:N 관계로 매핑된다.

이러한 특징 때문에 Master / Slave thread는 일종의 서버-클라이언트 관계가 된다.

따라서 Slave가 Master에 접근할 수 있는 권한을 가진 계정을 별도로 생성해야 한다. (이거 때문에 골머리좀 앓았다.)

 

Master thread 입장에선 Slave thread나 일반 application의 요청이나 똑같이 받아들인다.

즉, Slave thread가 Master thread로부터 바이너리 로그를 전달해달라는 요청을 할 때, "나 니 Slave thread야!" 라고 하는 것을 알려줄 무언가가 필요한데, 이 때 사용되는 것이 아래 프로토콜들이다.

COM_BINLOG_DUMP COM_BINLOG_DUMP_GTID
bin_log와 bin_log_position을 기반으로 position 선정 GTID를 기반으로 position 선정

bin_log_position은 bin_log를 저장할 때, bin_log 내 이벤트가 실행된 위치(오프셋)을 일컫는다.

하나의 타임라인이라는 메인 스트림이 있고, position은 이 타임라인 중 어디에 속하는지를 알려주는 친구라고 이해하자.

이러한 정보는 Slave DB에서 다음에 어디서부터 복제해야 할지 알아야 하는 매우 중요한 정보이므로, 바이너리 로그와 똑같은 역할을 하는 릴레이 로그에 저장된다.

 

3. Slave SQL thread의 병목 가능성과 이로 인한 데이터 정합성 문제

단순히 바이너리 로그를 읽고 릴레이 로그로 기록해주는 I/O thread와는 다르게, 직접적인 쿼리를 실행하는 SQL thread는 부하의 가능성이 다분하다.

Master DB에선 다수의 스레드로 쿼리를 실행하지만, 아무래도 read 역할만 수행하는 Slave DB는 상대적으로 Master DB에 비해 스펙이 부족하다.

이러한 문제를 해결하기 위해 MySQL 5.7에선 MTS(Multi Thread Slave) 라는 기능을 내놓았다.

다음과 같은 특징들이 존재한다.

- 최대 10배 이상의 속도 향상
- 4개 스레드인 MTS 기준으로, 단일 스레드 복제(STS)보다 3.5배 이상의 처리량을 제공
- 가급적이면 RBR 사용 권장, 속도는 낮지만 실행시간은 압도적으로 SBR보다 유리

 

4. 단점을 해결하려면?

비동기식의 단점은 정합성 문제와 최신 데이터 문제이다. 발생할 수 있는 문제와 해결방안은 다음과 같다.

 

4-1. 읽기 지연(Read lag)

Slave가 Master의 바이너리 로그를 읽는 시간이 너무 뒤처져서 정합성이 보장되지 않는 문제다.

이 경우, 아래 쿼리를 실행시켜 Seconds_behind_master 값을 확인해보고, 지연이 크다면 Slave의 I/O, SQL thread를 조정해 최적화 할 수 있다.

아래는 replication을 중단하다가 다시 시작한 Slave의 데이터다.

mysql> show slave status\G;
첫 실행
10초 후 실행
1분 후 실행

해당 값은 master보다 몇 초 뒤처져있는지를 나타내는 값으로, 이를 활용해 잘 최적화해보도록 하자.

 

4-2. 데이터의 유실 가능성과 최신 데이터의 모호함

Master로부터 출발한 복제 데이터가 Slave에 도달하기 전까지 Master에서 장애가 발생하면, 데이터가 유실된다. 이 경우 크게 두 가지 방법으로 해결이 가능하다.

 

반동기식 사용

반동기식 replication을 사용하면, 최소 1개의 Replica로부터 응답을 받아야만 트랜잭션을 종료하므로 장애가 발생해도 데이터의 유실이 발생하지 않는다.

다만 반동기식은 성능이슈가 있으므로 적법한 방법은 아닌 것 같다.

 

GTID 기반 replication

GTID가 또 나왔다. 앞서 언급한대로 GTID는 트랜잭션 고유 ID를 일컫는다, 따라서 Master와 Slave가 동일한 GTID를 보유하고 있으므로, 복제 과정에서 유실된 트랜잭션을 자동으로 감지하고 다시 실행할 수 있다.

에러가 발생해 최신 데이터를 찾아야 할 경우, bin_log 방식은 Slave가 어디까지 복제했는지 정확한 position을 찾기가 어렵다.

그러나 GTID 기반으로 진행할 경우, 복제되지 않은 트랜잭션을 자동으로 감지하고 추적 및 적용이 가능하다.

 

MySQL 5.5부터 지원하니까, 웬만하면 GTID 기반으로 replication하는 것이 바람직해 보인다. (5.7도 deprecated되는 세상이다.) 

결론


이렇게 replication의 원리와 동기/비동기식의 차이점, 각 단계별 세세한 작동방식에 대해서도 알아봤다.

원래 했던 작업이 migration 작업이었으므로, 다음 포스팅에선 Azure 기반의 MariaDB 10.2 -> MySQL 8.0 migration 방법에 대해 기술해해보겠다.

참고


MySQL Replication formats

GTID

MySQL - Replication 구조

Multi-threaded Replication Performance in MySQL 5.7

728x90
반응형