Skip to content

MySQL Replication 적용하기 (이론 구성)

Seungrae edited this page Oct 8, 2022 · 6 revisions

데이터베이스 이중화하기!

Replication이란?

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

그렇다면 Replication을 왜 사용할까? 대부분 사람들은 스케일 아웃을 통해 부하를 줄이는 효과가 있다는 것을 알고 있다.
그렇다면 내편은 그만큼의 부하가 있어서 적용하는 걸까? 물론 그만큼의 부하는 발생하지 않는다. Replication을 통해 얻을 수 있는 이점은 총 4가지로 볼 수 있다.

  • 스케일 아웃
  • 데이터 백업
  • 데이터 분석
  • 데이터의 지리적 분산

스케일 아웃

  • 갑자기 늘어나는 트래픽을 대응하는데 유연한 구조

데이터 백업

  • 레플리카를 안하더라도 백업을 해야한다.
  • 백업 과정은 실제 실행중인 쿼리들에 영향을 줄 수 있다.
  • 레플리카 서버에서 데이터 백업을 실행하여 소스 서버에서 백업시 발생하는 문제들을 해결

데이터 분석

  • 분석용 쿼리는 대량의 데이터를 조회하고 쿼리 자체가 무거운 경우가 많다.
  • 레플리카 서버에서 분석용 쿼리만 전용으로 하는 것이 좋다.

데이터의 지리적 분산

  • 데이터베이스와 애플리케이션 서버가 멀리 떨어져 있다면 응답을 늦게 받게 된다.
  • 빠른 응답을 위해 애플리케이션 서버에 가깝게 서버를 구성하는 것이 좋다.

내편팀에서 가져갈 수 있는 이점은 스케일 아웃, 데이터 백업 그리고 추가적으로 데이터 분석까지가 될 수 있겠다.
그렇다면 복제는 과연 어떻게 할까?

복제는 어떻게 하는가?

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

바이너리 로그

  • 데이터의 변경내역
  • 데이터베이스나 테이블의 구조 변경
  • 계정이나 권한의 변경정보

MySQL복제는 이러한 바이너리 로그를 기반으로 구현되었다. image

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

세 개의 스레드

  • Binary Log Dump Thread
  • Replication I/O Thread
  • Replication SQL Thread

Binary Log Dump Thread

  • Binary Log를 레플리카 서버로 전송하는 역할
  • 레플리카 서버가 소스 서버에 연결되면 소스서버에서 내부적으로 Binary Log Dump Thread를 생성

Replication I/O Thread

  • I/O 스레드는 소스 서버의 바이너리 로그 덤프 스레드로부터 바이너리 로그 이벤트를 가져와 로컬 서버의 파일(Relay Log)로 저장하는 역할
  • SHOW REPLICA STATUS 명령의 결과에서 Replica_IO_Running 컬럼에서 I/O 스레드의 현재 상태를 확인할 수 있다.

Replication SQL Thread

  • I/O 스레드에 의해 작성된 릴레이 로그 파일의 이벤트를 읽고 실행
  • SHOW REPLICA STATUS 명령의 결과에서 Replica_SQL_Running 컬럼에서 SQL 스레드의 현재 상태를 확인할 수 있다.

이렇게 복제하는 방식을 알아봤는데 그렇다면 변경 내용을 어떻게 식별할까?

어떻게 변경 내용을 식별하는가?

  • 바이너리 로그 파일 위치 기반
  • 글로벌 트랜잭션 ID 기반 (GTID) 기본적으로 MySQL 복제를 사용하려면 소스 서버에서 반드시 Binary Log가 활성화돼 있어야 한다. show master status;를 통해 바이너리 로그가 기록되는지 확인 할 수 있다.

Binary Log파일 위치 기반 복제

레플리카 서버에서 소스 서버의 바이너리 로그 파일명과 파일내에서 위치(Offset 또는 Position)로 바이너리 로그 이벤트를 식별해서 복제하는 방식이다. 복제에서 각각의 이벤트들이 바이너리 로그 파일명과 파일 내 위치 값의 조합으로 식별된다.

-> 문제는 이 같은 식별이 바이너리 로그 파일이 저장돼 있는 소스 서버에서만 유효하다는 것이다. 동일한 이벤트가 레플리카 서버에서도 동일한 파일명의 동일한 위치에 저장된다는 보장이 없다. 즉 장애가 발생했을때 장애 복구가 어렵다. 그렇다면 소스서버에서 발생한 각 이벤트들이 복제에 참여한 모든 MySQL 서버들에 동일한 고유 식별값을 가진다면 어떨까? 그렇다면 장애가 발생해도 좀 더 쉽게 장애를 복구할 수 있다. 이를 활용한 복제 방법이 GTID를 이용한 복제방법이다

글로벌 트랜잭션 아이디(GTID)

MySQL의 GTID는 서버에서 커밋된 각 트랜잭션과 연결된 고유 식별자로, 해당 트랜잭션이 발생한 서버에서 고유할뿐만 아니라 그 서버가 속한 복제 토폴로지 내 모든 서버에서 고유하다. 그래서 GTID기반으로 바이너리 로그의 이벤트를 가져와서 동기화하는 복제 방식이다. 장점으로는 크게 두가지이다.

  • 장애가 발생해도 손쉽게 복제 토폴리지를 변경할 수 있다.
  • 장애 복구에 소요되는 시간도 줄어든다.

따라서 내편팀은 GTID기반의 복제 방식을 택했다.

바이너리 로그는 어떻게 생겼을까?

크게 3가지 방식으로 구성할 수 있다.

  • Statement 방식
  • Row 방식
  • Mixed 방식

Statement 방식

  • SQL문을 바이너리 로그에 기록하는 방식이다.
  • 트랜잭션 격리 수준이 반드시 REPEAABLE-READ 이상이어야 한다.

장점

  • 손쉽게 SQL문을 확인할 수 있다.

단점

  • 비확정적(delete/update에 order by없이 limit 사용..등)으로 처리할 수 있는 쿼리가 실행된 경우 Statement 포맷에서는
    복제 시 소스 서버와 레플리카 서버간 데이터가 달라질 수 있다.

Row 방식

  • 데이터 변경이 발생했을 때 변경된 값 자체가 바이너리 로그에 기록되는 방식이다. (MySQL 5.7.7 버전 이후부터 바이너리 로그 기본 포맷이다.)

장점

  • 어떤 형태의 쿼리든지 복제시 소스서버와 레플리카 서버의 데이터를 일관되게 한다.

단점

  • 많은 데이터를 변경하면 모든 데이터가 전부 기록돼 바이너리 로그 파일이 단시간에 매우 커진다.

Mixed 방식

  • Statement 방식과 Row 방식 혼합

복제 동기화 방식

비동기 복제

  • 소스 서버가 레플리카 서버에서 변경 이벤트가 정상적으로 전달 됐는지 확인하지 않는다. image

반동기 복제

  • 소스 서버는 레플리카 서버가 소스 서버로부터 전달 받은 변경 이벤트를 릴레이 로그에 기록 후 응답을 보내면 그때 트랜잭션을 완전히 커밋한다. 스크린샷 2022-10-07 오후 3 30 22

소스, 레플리카 서버 어떻게 구성할까?

내편팀에서는 멀티 레플리카 복제로 구성했다. 그 이유는 분석용 쿼리는 실제 운영하는 db에 날리면 실제 부하를 줄 수 있는 위험이 있어서
따로 백업용으로 복제한 레플리카 서버를 활용하기 위해서 멀티 레플리카 복제를 택했다. image

그 이외에도

  • 싱글 레플리카 복제
  • 체인 복제
  • 듀얼 소스 복제
  • 멀티 소스 복제가 있다. 추가적으로 학습해보면 좋을 것 같다.

MySQL 복제 하는 법

일단 소스 서버와 레플리카 서버의 인스턴스와 MySQL이 설치되었다고 생각하고 진행하겠다.

설정

### 소스 서버
[mysqld]
gtid_mode=ON
enforce_gtid_consistency=ON
server_id=1
log_bin=mysql_bin

### 레플리카 서버
[mysqld]
gtid_mode=ON
enforce_gtid_consistency=ON
server_id=2
relay_log=/var/log/mysql/mysql-relay-bin
relay_log_purge=ON
read_only
log_slave_updates

gtid_mode=ON과 enforce_gtid_consistency=ON을 함께 명시해야 한다.   
만약 gtid_mode만 ON으로 설정되고 enforce_gtid_consistency가 설정되지 않으면 다음과 같은 에러가 발생하면서 MySQL 서버는 기동하지 않는다.

설정을 해줬다면 mysql 서버들을 재시작 해주면된다.

sudo systemctl stop mysql
sudo systemctl start mysql

복제 계정 준비

소스 서버에서 복제 계정을 준비해야 한다.

CREATE USER 'repl_user'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'%';
flush privileges;

보안을 위해서 꼭 필요한 IP 대역에서만 복제 연결이 가능하도록 호스트 제한에 % 대신 적절한 IP 대역을 설정하는 것이 좋다.

데이터 복사

우선 소스 서버의 데이터를 덤프해서 레플리카 서버에 적재해야한다

#### 덤프데이터 뽑기
mysqldump -uroot -p -v --all-databases --single-transaction --routines --set-gtid-purged=ON --triggers --extended-insert --master-data=2 > data.sql

#### 덤프데이터 이동
scp -i {key-file-name}.pem data.sql ubuntu@{ec2-ip}:/home/ubuntu

덤프데이터 넣기

sudo mysql -uroot -p
SOURCE /home/ubuntu/data.sql

그렇다면 이제 복제가 시작할 준비는 거의 마쳤다고 볼 수 있다. 이제 명령어를 통해 복제를 시작하면 된다. 레플리카 서버에 접속한 후 해당 명령어를 입력하면 된다.

STOP REPLICA;

MySQL 8.0.23 이상 버전
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='소스디비host',
SOURCE_PORT=3306,
SOURCE_USER='repl_user',
SOURCE_PASSWORD='password',
SOURCE_AUTO_POSITION=1,
GET_SOURCE_PUBLIC_KEY=1;

MySQL 8.0.23 미만 버전

CHANGE MASTER TO
MASTER_HOST='',
MASTER_PORT=3306,
MASTER_USER='repl_user',
MASTER_PASSWORD='password',
MASTER_AUTO_POSITION=1,
GET_MASTER_PUBLIC_KEY=1;

START REPLICA;

SHOW REPLICA STATUS\G;

SHOW REPLICA STATUS\G; 명령어를 실행했을때 Replica_IO_Running: YES Replica_SQL_Running: YES 가 정상적으로 되어 있으면 정상적으로 복제가 실행되는 것이다.

Clone this wiki locally