본문으로 바로가기

프로젝트의 전체 소스 코드는 이곳에서 확인하실 수 있습니다.

 

f-lab-edu/shoe-auction

개인 간 신발 거래 서비스. Contribute to f-lab-edu/shoe-auction development by creating an account on GitHub.

github.com

📖개요

현재 진행하고있는 Shoe-auction 프로젝트에서 MySQL 서버를 Master/Slave 구조로 이중화했던 과정을 소개하려고 한다. 처음에는 AWS 프리티어로 이용 가능한 micro급 서버를 선택 했으나, 성능테스트를 위해 대량의 Mock 데이터를 추가하는 과정에서 애플리케이션의 성능이 나오지 않아 무료 크레딧을 사용할 수 있는 NCP로 변경해서 서버를 구성했다.

NCP도 AWS의 RDS처럼 DB서버를 간편하게 구성할 수 있는 Cloud DB Server가 존재하지만, 가장 저렴한 서버 기준으로 한달 이용요금이 30만원 가까이 청구되고, 일시 정지 또한 불가능하기 때문에 직접 MySQL를 구성하고 Replication까지 적용해 보았다.

 

MySQL 설치하기

네이버 클라우드 플랫폼의 가장 큰 장점은 한글로된 문서라고 생각한다.  MySQL 서버 이미지 사용 가이드를 보고 서버에 MySQL을 설치해주자.

 

이번 포스팅에서 사용된 서버 스펙은 다음과 같다.

  • Compact급 서버 1vCPU 2GB 2대 (Master / Slave)
  • MySQL 5.7XX
  • CentOS 7.2

아래와 같이 Master서버 1대, Slave서버1대를 준비해야 한다.

Replication을 구성하기 전에 나중을 위해 미리 각 서버에 인코딩을 설정해두자.

아래 명령어를 통해 MySQL 서버에 접속

mysql -u root -p

mySQL의 기본 CharacterSet은 latin1이다. 이것을 UTF8로 변경해주자.

해당 명령어는 centOs기준으로, 우분투를 사용한다면 조금 다른 경로로 접근해야 한다.
vi /etc/my.cnf
[client]
default-character-set = utf8

[mysql]
default-character-set=utf8

[mysqldump]
default-character-set=utf8

[mysqld]
...생략
character-set-server=utf8
collation-server=utf8_general_ci
init_connect=SET collation_connection = utf8_general_ci
init_connect=SET NAMES utf8

my.cnf에 위 내용을 추가하고 아래 명령어를 통해 MySQL서버를 재시작 하고

systemctl restart mysqld

다시 MySQL 서버에 접속해서 status 명령어를 입력하면 다음과 같이 모든 CharacterSet이 UTF8로 수정된것을 확인할 수 있다.

1. Replication

Replication 2대 이상의 DB 서버가 동일한 데이터를 담도록 실시간으로 동기화하는 기술이다.

 

로컬 환경에서만 개발하거나 개인 토이 프로젝트를 진행할 때는 경험할 수 없는 부분이지만, 실제로 대용량 트래픽이 발생하는 애플리케이션 환경에서는 데이터베이스의 부하로 인한 병목 현상이 서비스 장애의 주된 원인이 된다.

 

따라서 2대 이상의 MySQL 서버가 모두 동일한 데이터를 가지면서 트래픽을 여러 MySQL 서버로 분산시켜주기 때문에 데이터베이스로 인한 병목 현상을 개선하는 데 도움을 준다.

 

대체로 Update/Insert/Delete와 같은 쓰기 작업은 Master DB에서 수행하고, Select와 같은 읽기 작업은 Slave DB에서 수행하도록 하여 트래픽 초과로 인한 서비스 장애 현상을 방지한다.

 

또한 기본적으로 1개의 Master 서버와 1개 이상의 Slave 서버로 구성하면서 Master 서버에 장애가 생겨 서버가 중지되었을 경우 Slave 서버 중 1개를 Master로 승격시켜 서비스의 중단 없이 데이터의 빠른 복구를 가능하게 하는 자동 failover 기능도 제공한다.

 

이번 프로젝트에서는 간단한 학습용으로 Master서버 1대, Slave 서버 1대로 진행했다.

2. 동작 방식

MySQL Replication의 동작 방식을 이해하기 전에 먼저 MySQL의 바이너리 로그의 개념을 알고 있어야 한다.

바이너리 로그란 Master 노드에서 DDL 또는 DML 가운데 데이터의 구조나 내용을 변경하는 모든 쿼리 문장과 이력을 기록하는 논리적인 로그를 의미한다.

1. Master 노드에서 일어나는 모든 변경 이력들을 바이너리 로그에 기록한다.

2. Slave 노드에서 변경 내역을 요청하면 Master 장비는 바이너리 로그을 읽어 Slave쪽으로 데이터를 전달한다. (Master의 Binlog dump Thread)

3. Slave 서버는 요청한 변경 내역을 Relay log에 기록한다 (Slave의 I/O Thread)

4. Relay log에 기록된 데이터들을 읽어서 Slave 노드에 적용한다. (Slave의 SQL Thread)

 

한 가지 주의할 점은 Binary log를 읽어서 Master -> Slave로 전송하는 과정에서 (그림 : 보라색 화살표) 비동기 방식으로 동작하기 때문에 전송이 완료되는 시점까지 기다려주지 않는다. 따라서 해당 과정 사이에 발생하는 select 작업에 있어서 데이터 정합성 문제가 발생할 가능성이 있다. (MySQL Repliaction은 기본적으로 비동기 방식)

단, InnoDB 스토리지 엔진은 Master와 Slave의 데이터 정합성 보장을 위해 넥스트 키 락(Next Key Lock)을 사용한다.

3. Replication 실습

1. Master 서버 접속

해당 포스팅은 Master 서버에 이미 Database가 존재한다고 가정하고 진행하도록 하겠다.

현재 Master 서버에 생성된 DB의 이름은 shoeauction이다.

 

먼저 Master 서버인 shoe-auction-db-master에 접속하자. 현재 윈도우를 사용하고 있기 때문에 리눅스 환경 접속을 위해 putty를 사용해서 Master 서버에 접속했다.

마스터 서버에 접속한 후 가장 먼저 위 명령어를 통해 Slave DB와 연결할 계정을 하나 생성하자.

예시) mysql > CREATE USER 'masterDB'@'%' IDENTIFIED BY 'masterDB@@@';

mysql > CREATE USER '생성할 계정명'@'%' IDENTIFIED BY '비밀번호';

이후 생성한 계정에 아래 명령어를 입력해 replication 권한을 부여하고 설정한 권한을 적용해야 한다.

mysql >  GRANT REPLICATION SLAVE ON *.* to '생성한 계정명'@'%' IDENTIFED BY '비밀번호';

예시) GRANT REPLICATION SLAVE ON . to 'masterDB'@'%' IDENTIFED BY 'masterDB@@@';

mysql > flush privileges;

exit로 MySQL을 종료후 my.cnf 파일을 수정해야 한다. vi 에디터를 통해 Master DB 서버의 my.cnf를 수정해보자.

vi /etc/my.cnf

위와 같이 [mysqld] 아래에 다음과 같은 내용을 추가한다.

log-bin=mysql-bin
server-id=1
binlog-do-db=DB이름

server-id는 서버의 고유ID로 1~2^32-1까지 임의의 숫자를 부여할 수 있지만 통상적으로 Master에는 1을, slave에는 1 이외에 숫자를 부여한다.

 

설정이 끝났으면 아래 명령어를 입력해 mysql 서버를 재시작 해주자.

# service mysqld restart

다음으로 Replication을 위해 Master Server에서 Slave Server로 dump를 전송해야 한다. 여기서 주의할 점은 dump파일을 생성하기 전에 MySQL 서버에 접속해서 Shared Lock을 걸어 읽기/쓰기를 차단해야 한다.

mysql > FLUSH TABLES WITH READ LOCK;

읽기 잠금을 걸었으면 다시 exit로 빠져나와서 아래 명령어들을 차례대로 입력한다.

# mysqldump -u '위에서 생성한 계정' -p DB명 > DB명.sql

예시) mysqldump -u 'masterDB' -p shoeauction > shoeaucion.sql

 

ls -al 명령어를 입력해 현재 디렉토리에 shoeauction.sql 파일이 생성되었는지 확인한다.

생성된 shoeauction.sql 파일을 Slave Server에 전송해야 한다.

# scp shoeauction.sql 'putty 접속 ID'@'슬레이브 서버의 비공인 IP주소':/root/

예시) scp shoeauction.sql 'root'@'111.222.333':/root/

 

전송이 완료되었으면 다시 MySQL 서버에 접속해서 아래 명령어를 통해 File명과 Position 값을 기록해두자.

mysql > SHOW MASTER STATUS;

mysql > UNLOCK TABLES;
2. Slave 서버 접속

가장 먼저 Master 서버에서 사용하는 DB와 동일한 이름의 DB를 Slave 서버에 생성하자.

mysql > create database shoeauction;

다음으로 마스터 서버에서 했던것과 동일하게 my.cnf 파일을 수정해야 한다.

# vi /etc/my.cnf
log-bin=mysql-bin
server-id=2 // master와 다른 server-id 사용
read_only = 1

다음으로 Master 서버에서 전송한 mysqldump 파일을 받아와야 한다. 참고로 slave DB는 따로 계정을 생성하지 않고 기존의 root 계정을 사용했다.

# mysql -u root -p shoeauction < shoeauction

다음으로 Slave DB 서버에 접속해서 Master DB 서버의 정보를 입력해야 한다.

mysql > CHANGE MASTER TO MASTER_HOST='마스터 서버의 공인 IP주소', MASTER_PORT=3306, MASTER_USER='위에서 생성한 계정', MASTER_PASSWORD='비밀번호', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=39278;  

위 명령어를 입력한 후 아래 명령어를 실행하면 Replication 구성이 완료된다.

mysql> start slave;

4. 적용 확인 및 에러 해결

아래 명령어를 입력해서 정상적으로 Replication이 적용되었는지 확인해보자.

mysql> show slave status\G;
mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 111.111.111.111
                  Master_User: shoeauction
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 39278
               Relay_Log_File: s1793c2d433c-relay-bin.000003
                Relay_Log_Pos: 38995
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
      ... 생략

설정 정보들이 쫘르륵 나오는데 Slave_IO_Running: Yes / Slave_SQL_Running: Yes 가 출력된다면 정상적으로 Replication이 적용된 것이다.

만약 둘중 하나라도 NO가 출력된다면 정상적으로 적용되지 않은것이다. 가장 대표적인 에러로 다음과 같은 에러메시지가 있다.

Last_IO_Error: Fatal error: The SLAVE I/O thread stops because MASTER AND SLAVE have equal MySQL SERVER UUIDs; these UUIDs must be different FOR REPLICATION TO work.

해당 에러는 Slave DB 서버의 auto.cnf 파일에 현재 서버의 UUID가 적혀있는데, 이 파일까지 Master DB 서버에서 함께 가져와서 발생하는 에러다.

 

다음 명령어를 차례대로 실행해서 에러를 해결해보자

# systemctl stop mysqld
# rm -rf /var/lib/mysql/auto.cnf
# systemctl start mysqld

이후 다시 아래 명령어를 입력하면 정상적으로 적용된것을 확인할 수 있을것이다.

mysql > start slave;
mysql > show slave status\G;

 

마지막으로 간단하게 Replication이 제대로 동작하는지 확인해보자.

application.yml
spring:
  datasource:
    url: jdbc:mysql://마스터 서버 공인IP:3306/shoeauction?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC
    username: 
    password: 
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: create

기존에 사용하던 H2 DB대신 Master DB Server로 datasource를 교체하고 ddl-auto를 create 설정 후 애플리케이션을 실행하면 다음과 같이 Master와 Slave에 각각 테이블이 생성되는것을 확인할 수 있다.

 


Shoe-auction 프로젝트에서는  @Transactionl(readOnly=true)적용된 메소드에 대해서는 Slave DB에서 읽기 작업을 수행하고,  @Transactional이 적용된 메소드에 대해서는 Master DB에서 쓰기/읽기 작업이 수행되도록 구현하였다. 

 

[Spring] Master/Slave DataSource 동적 라우팅 설정하기

 현재 로컬에서 진행중이던 프로젝트가 배포 단계로 진입함으로써 DBMS 또한 테스트용 h2에서 RDS에 MySql을 올려 사용하는 방식으로 교체하였다.  현재 진행중인 프로젝트인 shoe-auction은 사용자가

kerobero.tistory.com

 


http://cloudrain21.com/mysql-replication

 

MySQL - Replication 구조 - Rain.i

All about IT tech, especially database, cloud, linux, clustering.

cloudrain21.com

http://www.yes24.com/Product/Goods/6960931

 

Real MySQL

Real MySQL, MySQL의 새로운 발견!더 이상 MySQL은 커뮤니티나 소셜 네트워크 서비스와 떼어놓을 수 없는 관계에 있다는 것은 누구나 잘 알고 있을 것이다. 하지만 MySQL은 여기서 그치지 않고 빌링이나

www.yes24.com