Database/SQL

Real MySQL - Replication(복제)란 무엇인가?!

JaeHoney 2022. 7. 7. 08:37

데이터베이스를 사용할 때 가장 중요한 두 가지는 확장성(Scalability)과 가용성(Availability)이다.

 

서비스에서 발생하는 대용량 트래픽을 안정적으로 처리하기 위해서는 서버의 확장이 필수적이며, 사용자가 언제든지 안정적인 서비스를 이용할 수 있게 하려면 DBMS 서버를 포함한 하위 시스템들의 가용성이 반드시 뒷받침되어야 한다.

 

이 두 요소를 위해 가장 일반적으로 사용하는 기술이 복제(Replication)이다.

복제

복제(Replication)는 한 서버에서 다른 서버로 데이터가 동기화 되는 것을 말한다. 원본 데이터를 가진 서버를 소스(Source) 서버, 복제된 데이터를 가지는 서버를 레플리카(Replica) 서버라고 부른다.

 

소스 서버에서 데이터가 변경되면 레플리카 서버에서는 변경 내역을 소스 서버로부터 전달받아 자신의 데이터에 반영함으로써 소스 서버와 데이터를 동기화시킨다.

 

복제를 사용하는 대표적인 이유에 대해서 삵펴보자.

  • 스케일 아웃(Scale-out)
    • 사용자가 늘어남에 따라 커지는 트래픽으로 인한 DB 서버의 부하를 분산할 수 있다.
    • 갑자기 늘어나는 트래픽에도 유연하게 대처할 수 있다.
  • 데이터 백업
    • 의도치않게 데이터가 삭제되면 백업을 진행하게 된다.
    • 동일한 서버 내에서 백업이 실행되는 경우 DB서버의 부하가 크게 발생한다.
    • 데이터 백업을 레플리카 서버에서 실행한다.
  • 데이터 분석
    • 서비스에서 사용되는 쿼리가 아닌 차세대 비즈니스 모델 발굴을 위해 분석용 쿼리를 실행하기도 한다.
    • 대량 데이터 조회 및 집계 연산도 많기 때문에 실제 DB 서버에는 부하가 없도록 한다.
    • 여분의 레플리카 서버를 분석용 쿼리 전용으로 사용할 수 있다.
  • 데이터의 지리적 분산
    • 서비스에서 사용되는 애플리케이션 서버가 DB 서버와 장거리로 떨어져있을 수도 있다.
    • 이 경우 두 서버의 통신 시간은 거리에 비례한다.
    • 애플리케이션 서버와 가까운 위치에 레플리카 서버를 구축해서 응답 시간을 개선한다.
  • 소스 서버가 문제가 생겼을 때 대체 서버의 역할을 한다.

아키텍처

MySQL 서버에서 발생하는 모든 변경사항은 별도의 로그 파일에 순서대로 기록되는데, 이를 바이너리 로그(Binary Log)라고 한다.

 

MySQL에서 복제는 소스 서버에서 생성된 바이너리 로그가 레플리카 서버로 전송되고 이를 레플리카 서버가 반영함으로써 데이터 동기화가 이뤄진다. 레플리카 서버에서 소스 서버에게 받은 바이너리 로그를 저장해둔 파일을 릴레이 로그(Relay Log)라고 한다.

스레드 동작

MySQL의 복제는 세 개의 스레드에 의해 작동하는데, 이 세 스레드 중 하나는 소스 서버에 존재하며 나머지 두 개의 스레드는 레플리카 서버에 존재한다.

  • 바이너리 로그 덤프 스레드(Binary Log Dump Thread)
    • 레플리카 서버가 소스 서버에 연결되면 소스 서버에서 해당 스레드를 생성
    • 바이너리 로그의 내용을 레플리카로 전달한다.
    • 레플리카 서버로 보낼 각 이벤트를 읽을 때 일시적으로 바이너리 로그에 잠금을 수행한다..
  • 레플리케이션 I/O 스레드(Replication I/O Thread)
    • 바이너리 로그 이벤트를 가져와서 레플리카 서버의 릴레이 로그에 저장한다.
    • 소스 서버의 바이너리 로그를 읽어서 파일로 쓰기 때문에 I/O 스레드라 명명
  • 레플리케이션 SQL 스레드(Replication SQL Thread)
    • I/O 스레드에 의해 작성된 릴레이 로그 이벤트들을 읽고 실행한다.
    • 즉, 레플리케이션 SQL 스레드가 작동하면서 실질적인 동기화가 이루어진다.

복제 데이터

  • 릴레이 로그
    • 레플리케이션 I/O 스레드에 의해 작성되는 파일이다. 소스 서버의 바이너리 로그에서 읽어온 이벤트(트랜잭션) 정보가 저장된다.
    • 현재 존재하는 릴레이 로그 파일들의 목록이 담긴 인덱스 파일과 이벤트 정보가 저장돼 있는 로그파일들로 구성된다.
  • 커넥션 메타데이터(Connection Metadata) 
    • 레플리케이션 I/O 스레드에서 소스 서버에 연결할 때 사용하는 DB 계정 정보 및 소스 서버의 바이너리 파일명과 파일 내 위치 값 등이 담겨있다.
  • 어플라이어 메타데이터(Applier Metadata)
    • 최근 적용된 릴레이 이벤트에 대해 해당 이벤트가 저장되어 있는 릴레이 로그 파일명과 파일 내 위치 정보를 담고 있다.
    • 레플리케이션 SQL 스레드는 이 정보들을 바탕으로 레플리카 서버에 나머지 이벤트들을 적용한다.

추가로 기존에는 MySQL의 시스템 변수인 master_info_repository와 relay_log_info_repository를 FILE과 TABLE 두 가지로 중 한 가지로 선택하여 저장할 수 있었다. 그런데 FILE로 설정하면 커넥션 메타 데이터와 어플라이어 메타데이터가 동기화되지 않는 문제가 종종 발생했고, 이제는 해당 시스템 변수의 기본 값이 TABLE로 변경되었고 FILE 타입은 향후 삭제될 예정이다.

MSR

일반적으로는 Master DB 즉 Source DB에  Slave DB가 여럿 붙게 됩니다. 따라서 쓰기 동작을 하는 DB는 하나만 존재하게 된다.

 

이때 일반적으로 쓰기 작업의 부하 분산을 위해서 샤딩을 하게 된다. (이번 글에서는 다루지 않는다.)

 

MySQL에서는 쓰기의 부하 분산을 위해 Multi Source Replication (MSR)을 지원한다.

MSR을 사용하면 DB 1번과 DB2번에 각각 데이터를 저장하고, 조회는 한 번에 Slave에서 할 수 있다.

 

MSR을 구성할 때 Replica 서버에서는 Connection metadata repository 와 applier metadata repository에 대한 정보가 Table에 저장한다.

mysql> show variables like '%repository%';
+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| master_info_repository    | TABLE |
| relay_log_info_repository | TABLE |
+---------------------------+-------+
2 rows in set (0.25 sec)

 

모든 서버에서 server_id를 설정하는데 MASTER와 SLAVE가 각각을 구분할 수 있어야 한다.

mysql> show variables like 'server_id';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 64    |
+---------------+-------+
1 row in set (0.19 sec)

그리고 각 Master에서 Slave가 Master로 접근하기 위한 유저를 생성한다.

mysql> CREATE USER 'replUser'@'172.17.0.5' IDENTIFIED BY '!dlatl00';
Query OK, 0 rows affected (0.03 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO 'replUser'@'172.17.0.5';
Query OK, 0 rows affected (0.02 sec)

이제 Replication을 설정을 추가하자.

mysql>  CHANGE REPLICATION SOURCE TO
    ->  MASTER_HOST='172.17.0.6',
    ->  MASTER_USER='replUser',
    ->  MASTER_PASSWORD='!dlatl00',
    ->  MASTER_LOG_FILE='mysql-bin.000010',
    ->  MASTER_LOG_POS=716
    ->  FOR CHANNEL 'master_1';
Query OK, 0 rows affected, 2 warnings (0.03 sec)

mysql>  CHANGE REPLICATION SOURCE TO
    ->  MASTER_HOST='172.17.0.7',
    ->  MASTER_USER='replUser',
    ->  MASTER_PASSWORD='!dlatl00',
    ->  MASTER_LOG_FILE='mysql-bin.000010',
    ->  MASTER_LOG_POS=714
    ->  FOR CHANNEL 'master_2';
Query OK, 0 rows affected, 2 warnings (0.05 sec)

mysql>
mysql>  CHANGE REPLICATION SOURCE TO
    ->  MASTER_HOST='172.17.0.8',
    ->  MASTER_USER='replUser',
    ->  MASTER_PASSWORD='!dlatl00',
    ->  MASTER_LOG_FILE='mysql-bin.000011',
    ->  MASTER_LOG_POS=388
    ->  FOR CHANNEL 'master_3';
Query OK, 0 rows affected, 2 warnings (0.04 sec)

이제 SLAVE DB를 실행하면 된다.

START REPLICA FOR CHANNEL 'master_1';
START REPLICA FOR CHANNEL 'master_2';
START REPLICA FOR CHANNEL 'master_3';

# 또는 START REPLICA;

MSR은 일반적으로 많이 사용하는 기능은 아니다. 아직은 조회 쿼리가 너무 많이 발생하면 Replication이 일시적으로 딜레이가 발생하는 이슈나 대량의 트랜잭션을 처리할 때 동기화를 중단하는 이슈가 있다.


Reference