MySQL Replication(복제) 구성 및 설정 - Async - Semi Async

Share

Last Updated on 1월 19, 2024 by Jade(정현호)

부하분산 및 고가용성 확보

리플리케이션(Replication)은 데이터베이스를 복제를 의미하며 복제 대상인 Master 가 1개 또는 다수일 수 있으며 Slave(Standby) 서버에서 복제 동기화를 수행하여 사용함 것을 의미합니다.  
* DBMS에 따라 Primary, Standby 등의 표현이 있습니다.
* MySQL 8 버전 부터는 Source(소스), Replica(리플리카) 로 표현하고 있습니다.

장애를 대비하기 위한 고가용성 측면과 Read Write Query 와 Read Only 쿼리를 분리하여 수행함에 따른 부하 분산 및 백업 수행 등의 목적으로 사용합니다.  
      

MySQL의 Replication 방식

MySQL의 복제 방식에는 구성에 따라서 여러 가지로 정의할 수 있으며 여기 글에서는 Async 와 Semi-Sync 대해서 확인해 보도록 하겠습니다.
     

Async Replication

첫번째는 먼저 Async Replication 입니다.

MySQL은 기본적으로 예전부터 사용한 Async Replication 을 사용하게 됩니다.

Binary Log에는 MySQL의 모든 변경 사항이 기록이 되고 Slave DB에서는 이 Binary Log(이하 binlog) 를 이용하여 데이터 복제가 수행됩니다.

참고) Oracle에서는 이런 역할을 RedoLog 와 ArchiveLog file이 수행하고 MySQL의 InnoDB의 Redo와 Oracle의 Redo의 쓰임새에 차이가 있습니다.

InnoDB의 Buffer내 Dirty Page를 Disk로 flush 하기전에 Redo log buffer에서 Redo Log File로 먼저 기록되고 그후에 체크포인트가 발생되면 dirty page를 disk로 flush 하게 됩니다.

그래서 MySQL 인스턴스가 Crash가 발생하였을 때 재기동을 하면서 Instance Recovery 시 Redo 와 Undo 정보를 토대로 Roll-back과 Roll-forward가 수행됩니다.


MySQL에서 복제 환경에서는 3개의 스레드가 생성 및 시작됩니다.

• Binary log dump thread: Master(Source) 서버에 Slave(Replica) 서버가 연결되면(START SLAVE) 바이너리 로그 내용을 Slave(Replica) 서버로 보내는 Binlog Dump 스레드를 생성합니다. 이 스레드는 Master(Source) 서버에서 SHOW PROCESSLIST 명령어의 출력 내용에서 ​​Binlog Dump 스레드로 확인됩니다.
Slave(Replica)서버의 I/O Thread 요청에 의해 Master(Replica) 서버의
Binlog Dump 스레드가 바이너리 로그를 읽어서 Slave(Replica)에 전송합니다.

<MySQL Async Replication>

• I/O thread: START SLAVE(START REPLICA) 명령어가 Slave(Replica) 서버에서 실행되면 Slave(Replica) 서버는 Master(Source) 서버에 연결하고 바이너리 로그에 기록된 변경 내역을 보내도록 요청하는 I/O 스레드를 생성합니다.
I/O 스레드는 Master(Source)서버의 Binlog Dump 스레드가 보내는 변경 내역을 읽고 이를 Slave(Replica) 서버의의 릴레이 로그(relay log)에 기록합니다.

• SQL thread: Slave(Replica) 서버에서 I/O 스레드가 작성한 릴레이 로그를 읽고 여기에 포함된 트랜잭션을 실행하기 위해 SQL 스레드를 생성하고 SQL 스레드는 릴레이 로그를 읽어서 Slave(Replica) 서버에 반영합니다.


추가적으로 Slave에서 log_slave_updates 파라미터가 활성화(ON) 하게 되면 자신의 Binlog 에도 기록하게 됩니다.
log_slave_updates 파라미터는 Slave(Replica)에서 Master(마스터)로 부터 수신한 변경 내용에 대해서 자신(Replica/Slave)의 바이너리 로그에 기록합니다

이렇게 되면 A->B->C 형태로 중간의 B 인 Slave 서버가 C의 Master 역할도 수행할 수 있게 됩니다.
(체이닝 리플레케이션)


이렇게 Replication 되는 과정에서 기본적으로 Async(비동기) 방식으로 동작하기 때문에 Master 에서는 Slave의 복제 완료(성공) 여부 등을 확인하지 않고 Master 서버 중인 트랜잭션을 종료 및 결과 반환하게 됩니다.

그래서 Master(Source) 에서의 데이터 변경량이나 Slave서버의 부하 정도, 시스템 사용률, 네트워크 속도 등 여러가지 이유로 Slave에서 복제가 늦어질 수도(복제 지연) 있고 그에 따라 조회시점에 데이터의 차이가 발생될 수도 있습니다.

또한 Master DB의 문제로 인하여 복제가 완료되지 않은 트랜잭션은 손실이 될 수도 있긴 합니다.

Slave DB는 인스턴스 Crash 등의 문제가 발생될 경우, 인스턴스를 재시작 하게 되면 복제가 중지된 Binlog Pos 이후 부터 복제를 따라가면서 다시 복제가
 수행되기 때문에 보통의 경우 Slave DB의 장애는 큰 영향을 주지는 않습니다.

MySQL Replication에서는 Master / Slave 간의 복제 Gap을 줄이는 거나 발생하지 않게 하는 것이 중요합니다.
여담으로 AWS의 Aurora MySQL Cluster는 일반적인 MySQL Replication 방식이 아닌
 
자체 구현한 방식을 통해 수행되며, 이 방식을 사용하면 일반적인 MySQL Replication에 비해 더 빠르게 적용 및 복제 지연이 매우 감소하게 됩니다.


[참고] MySQL에서 바이너리 로그와 Redo Log 쓰기 관한 보다 자세한 사항은 아래 포스팅을 참조하시면 됩니다.

       

Semi-sync Replication

두번째로 Semi-sync Replication 입니다.

MySQL 5.5 버전에서 도입된 Semi-sync Replication 방식은 Master DB에서 Slave 로 전달된 Relay log의 기록이 완료되었다는 메시지(신호)를 받고나서
 처리중인 transaction의 결과를 요청한 application(client)에 결과를 반환해주는 방식입니다.



<MySQL Semi-sync Replication>


Async(비동기) 방식에 비하면 성능이 조금 저하되긴 하지만 완전한 Sync(slave에서 relay log 내용까지 적용되는) 방식보다는 성능 저하가 덜 하고 Slave 측까지는 일단 변경 사항이 전달되었기 때문에 Master 와 Slave 사이의 데이터에 대한 동기화 나 안전성 측면에서 Async 방식에 비해 더욱 보장해줄 수 있게 됩니다.

Semi-sync Replication 은 전달 시기(방법)에 따라서 after_commit(ver 5.5)after_sync(ver 5.7) 방식으로 나뉘게 됩니다.

Semi-sync Replication은 다음 포스팅에서 내용이 정리되어 있습니다.

                        

binlog 방식(바이너리 로그 방식)

Binary log(바이너리 로그)는 DDL 또는 DML 같이 MySQL 데이터베이스 내에서 발생하는 변경 내역이 저장되는 로그 파일입니다.
트랜잭션 커밋시 기록되며 기본적으로 변경 순서를 보장하여 기록합니다.


이런 MySQL의 변경 내역(이벤트)이 기록되는 로그 파일인 binary log(바이너리 로그) 대해서는 format을 설정할 수 있습니다.

binary log format 3가지 종류가 있습니다
binlog_format = row | mixed | statement

  • statement : 전통적으로 사용하던 방식으로 실행된 쿼리 구문 그대로 Binlog에 남겨지는 방식입니다.
  • row : 생성/변경된 데이터의 Before/After row image가 binlog에 저장하여, 사이즈가 statement 보다 크게 생성됩니다.
         5.1 버전에서 도입되었으며 5.7.7 버전부터 기본값이 되었습니다.
  • mixed : row와 statement 중간 형태로 날짜와 같이 determine 이 필요한 경우 row 형태로 저장되는 형식입니다.


binlog_format 을 row 로 설정시 연관되어 사용할 수 있는 파라미터로 binlog_row_image 이 있습니다.

row 방식으로 binlog 를 기록할 때 로깅할 컬럼 set을 설정할 수 있습니다.
binlog_row_image = FULL | MINIMAL | NOBLOB

  • full : 변경 전/후 이미지 전체를 binlog에 기록 (Default)
  • minimal : 최소한의 값만 로깅,변경된 행을 식별하는 데 필요한 이전 이미지의 열만 기록
  • noblob : Blob, text와 같이 사이즈가 큰 값을 제외하고 기록(로깅)


현재 테스트 환경은 mysql 5.7.31(Source Compile) 이기 때문에 binlog_format=row , binlog_row_image=full 이 default 로 사용되게 됩니다
               

마스터 설정 변경 및 유저 생성

/etc/my.cnf 파일에 아래 와 같이 내용을 추가/변경합니다.


my.cnf 파일 변경

[root]# vi /etc/my.cnf


[mysqld]
server-id=1
     -> server-id는 master와 slave가 달라야 합니다
### Replication
log-bin=/usr/local/mysql/logs/binlog
     -> Binlog의 파일명을 기재합니다.
sync_binlog=1
binlog_cache_size=2M
max_binlog_size=512M
expire_logs_days=7
log-bin-trust-function-creators=1


파일을 저장한 후 mysql을 재기동하여 binlog 를 활성화 합니다
[root]# systemic restart mysqld


* 상황에 따라 파라미터는 추가/변경하여 사용하시면 됩니다.


MySQL 기동 후 binlog 활성화 여부는 show variables 로 확인 가능 합니다.
show variables like '%log_bin%';

show variables 로 확인되는 log_bin 값과 my.cnf 에 설정한 log_bin 은 다른 내용이 입니다.

show variables 의 log_bin 은 현재 binlog의 활성화 유무만 보여주는 변수이고
my.cnf 에 기술되는 log_bin은 binlog를 활성화시키면서 경로까지 지정해주는 파라미터입니다.

sync_binlog 파라미터는 MySQL 서버가 바이너러 로그를 디스크에 동기화하는 빈도를 제어합니다.

  • sync_binlog=0
    MySQL 서버에서 디스크에 대한 바이너리 로그의 동기화(synchronization)를 비활성화합니다.
    대신 MySQL 서버는 운영 체제에 의존하여 다른 파일과 마찬가지로 바이너리 로그를 주기적으로 디스크로 플러시합니다.
    이 설정은 최고의 성능을 제공하지만 정전 또는 운영 체제 문제(Crash)가 발생한 경우 MySQL 서버는 트랜잭션을 커밋을 하였지만 바이너리 로그에는 동기화가 되지 않을 수 있습니다.
    sync_binlog=0으로 설정하게 되면, Binary log의 Disk 동기화는 MySQL이 아닌 운영체제에 의해서 진행되며(처리되며), Filesystem Cache Layer 까지만 쓰게 됩니다.
  • sync_binlog=1 (default)
    sync_binlog 파라미터의 기본값은 1 이며, 트랜잭션이 커밋되기 전에 바이너리 로그를 디스크에 동기화합니다. 이것은 가장 안전한 설정이지만 디스크 쓰기 횟수가 증가하여 성능에 부정적인 영향을 미칠 수 있습니다.
    정전이나 운영 체제 문제(Crash)가 발생한 경우 바이너리 로그에서 누락된 트랜잭션은 준비된 상태로 유지됩니다.
    이렇게 되면 자동 복구 루틴이 이러한 트랜잭션을 롤백할 수 있으므로 바이너리 로그에서 트랜잭션이 손실되지 않음을 보장하게 됩니다.
  • sync_binlog=N
    1보다 큰 값 n 으로 설정되면 n개의 바이너리 로그 커밋 그룹 마다 동기화가 수행됩니다.
    여기서 N는 0 또는 1이 아닌 값이며 N 바이너리 로그 그룹 커밋이 수집된 후 바이너리 로그가 디스크에 동기화 됩니다.
    정전이나 운영 체제 문제(Crash)이 발생한 경우 MySQL서버가 바이너리 로그에 동기화(플러시)되지 않은 트랜잭션을 커밋했을 수 있습니다.
    파라미터 설정 값이 높을수록 성능이 향상되지만 데이터 손실 위험이 높아집니다.



replication 계정 생성

[root]# mysql --login-path=dba

--login-path 관련해서는 아래 내용을 참고하시면 됩니다.


mysql> create user 'repl_user'@'%' identified by '패스워드';
mysql> grant replication slave on *.* to 'repl_user'@'%';
          

Slave 파라미터 설정

Slave(Replica) 서버의 my.cnf 파일을 아래와 같이 추가/수정합니다.

[root]# vi /etc/my.cnf

[mysqld]
server-id=2
   -> master 서버에서의 id 와 달라야 합니다.
#Slave Replication Option
report-host=acs2
   -> master 에서 status 조회시 사용
relay-log=/usr/local/mysql/logs/relay_log
relay_log_purge=true
expire_logs_days=7
log_slave_updates=ON

read_only


"report-host=호스트명" 은 master 서버에서 show slave hosts 명령어 수행시 host 정보를 확인할 수 있게 해주는 파라미터입니다.

= 파라미터 미설정시 
+-----------+-------+------+-----------+-------------------------------------------+
| Server_id |  Host | Port | Master_id |             Slave_UUID                    |
+-----------+-------+------+-----------+-------------------------------------------+
|    2      | [공란] | 3306 |     1     |    8687edf5-c2b7-11ea-bc27-080027a227aa   |
+-----------+-------+------+-----------+-------------------------------------------+

= 파라미터 설정시
+-----------+------+------+-----------+------------------------------------------+
| Server_id | Host | Port | Master_id |              Slave_UUID                  |
+-----------+------+------+-----------+------------------------------------------+
|    2      | acs2 | 3306 |     1     |   8687edf5-c2b7-11ea-bc27-080027a227aa   |
+-----------+------+------+-----------+------------------------------------------+


Slave에서 log_slave_updates 파라미터가 활성화(ON) 하게 되면 자신의 Binlog 에도 기록하게 됩니다.
log_slave_updates 파라미터는 Slave(Replica)에서 Master(마스터)로 부터 수신한 변경 내용에 대해서 자신(Replica/Slave)의 바이너리 로그에 기록합니다

MySQL 5.7 버전까지의 log_slave_updates 파라미터 기본값은 OFF이며, MySQL 8.0 버전 부터 기본값은 ON 입니다.

[참고] MySQL 8.0.26 버전 부터 log_slave_updates 에서 log_replica_updates 으로 명칭 변경되었습니다.
            

Master 데이터 추출 및 적재

mysqldump 데이터 추출 (Master)

[root]# mysqldump --login-path=dba --opt --single-transaction \
--hex-blob --master-data=2 --routines --triggers \
--all-databases | gzip > master_data.sql.gz

추출된 master_data.sql.gz 파일을 slave 서버로 전송합니다.


데이터를 입력합니다(slave)

[root]# gunzip master_data.sql.gz
[root]# mysql --login-path=dba < master_data.sql
        

Replication 설정 및 시작

SQL 파일 상단에서 binlog 파일명log position을 확인합니다.

[root]# more master_data.sql

파일에서 아래부분을 참조
CHANGE MASTER TO MASTER_LOG_FILE='binlog.000001', MASTER_LOG_POS=154


master 서버 정보 입력 (slave 에서 실행)

[root] mysql --login-path=dba


mysql> change master to
master_host='acs',
   -> Master 서버의 호스트명이나 ip

master_port=3306,
   -> Master 서버의 포트번호

master_user='repl_user',
   -> replication 계정명

master_password='패스워드',
     -> replication 계정 비밀번호

master_log_file='binlog.000001',
    -> 위에서 확인한 binlog 파일명

master_log_pos=154;
   -> 위에서 확인한 log position 번호



replication 시작

mysql> start slave;



slave 상태 조회

mysql> show slave status\G
********************* 1. row *********************
Slave_IO_State: Waiting for master to send event
Master_Host: acs
Master_User: repl_user
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: binlog.000001
Read_Master_Log_Pos: 154
Relay_Log_File: relay_log.000002
Relay_Log_Pos: 317
Relay_Master_Log_File: binlog.000001
Slave_IO_Running: Yes  <-- Slave에서 기동되는 Thread가 정상 기동중임
Slave_SQL_Running: Yes <-- Slave에서 기동되는 Thread가 정상 기동중임
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 154
Relay_Log_Space: 518
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
  <-- Master 서버와의 복제 Gap(Delay) 정보

Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: 12416d15-0bfa-11eb-ad17-080027d4f8ed
Master_Info_File: /usr/local/mysql/data/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates  
  <-- 동기화가 완료되어 추가 변경 event를 기다리는 중

Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)


Replication 중지(stop)

mysql> stop slave;
명령어를 통해서 복제를 중지할 수 있습니다
start slave 명령어를 수행하면 복제가 다시 재개됩니다.

복제를 중지 한 후 show slave status\G 로 조회해보면 2개의 thread 가 No 로 확인 됩니다
Slave_IO_Running: No 
Slave_SQL_Running: No 


Replication 마스터 정보 삭제

mysql> stop slave;
mysql> reset slave all;

        

Replication master 정보

Note (Code 1759): Sending passwords in plain text without SSL/TLS is extremely insecure.

Note (Code 1760): Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended.

Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.


테스트 환경에서는 change master 수행시 2가지 warning 이 발생되는데 SSL 미사용에 따른 메세지와 replication 유저의 정보가 평문으로 저장됨으로 주의하고, start slave 명령어를 이용하라는 메시지입니다.

ssl은 제외하고 두번째 메세지에 대해서 더 확인해보면 Master 정보가 포함된 파일을 열어보면 아래와 같이 평문으로 내용이 확인되고 그에 따라 계정 정보 또한 확인이 되게 됩니다.

[root]# cat /usr/local/mysql/data/master.info
25
binlog.000001
154
acs
repl_user  <-- ID
repl_user  <-- Password
3306
60
0

이와 같이 사용하는 것은 보편적인 방법이고 이렇게 평문으로 남기 때문에 Replication 권한만 부여된 유저를 생성하여 사용하는 것이기도 합니다.

warning 메세지에 나온 내용대로 start slave 명령어 를 활용할 수도 있습니다.

start slave 으로 사용하려면 change master 명령어시 ID/PW 정보는 입력하지 않습니다.
mysql> change master to
master_host='acs',
master_port=3306,
master_log_file='binlog.000001',
master_log_pos=154;


start slave 시 계정정보를 입력합니다.
mysql> START SLAVE user='repl_user' password='repl_user';

이렇게 수행 후 다시 master.info 파일을 살펴보면 아래와 같이 계정정보는 제외되고 저장된 것을 확인할 수 있습니다.

[root]# cat /usr/local/mysql/data/master.info

25
binlog.000001
154
acs


3306

60
0
          

Slave restart

Slave DB 인스턴스 재시작시 자동으로 Replication 이 시작(활성화) 됩니다.

이와 관련해서는 skip-slave-start 파라미터가 있으며 MySQL 기동 시 Slave Replication 이 자동으로 시작되는 것을 막기 위한 설정입니다
파라미터가 설정되어 있으면 mysql 기동 후 명시적으로 start slave; 를 해야 복제가 시작됩니다.


위에서 설명한 START SLAVE 구문에 계정정보를 입력하여 replication 사용시, Slave DB가 재가동 되었을 때 default 설정으로 replication이 가동되지만 master.info 파일에 계정 정보가 없어서 Replication 이 즉시 수행되지 못한다는 단점이 있습니다.

Last_IO_Error: Fatal error: Invalid (empty) username when attempting to connect to the master server. Connection attempt terminated.


show slave status\G 로 조회시 위와 같은 상태 메세지가 확인되며 사용자가 명시적으로 "start slave 계정정보" 명령어를 수행해야 replication 이 진행됩니다.

START SLAVE user='repl_user' password='repl_user';

즉, Slave db 재가동시 사용자가 start slave 를 챙겨줘야 하는 단점 있습니다.
사용시에는 이런 부분도 확인이나 고려가 되어야 할 것입니다.
     

정보 확인 및 모니터 명령어

# mastser
mysql> show master status;
mysql> show slave hosts;

# slave 
mysql> show slave status\G

Master DB에서 데이터 추가나 변경 등을 발생시켜서 데이터 동기화가 정상적으로 수행되는지 등을 추가로 테스트를 진행하시면 되겠습니다.


이어지는 다음 글:  MySQL Replication 설정 - Semi Async - after_sync - after_commit



연관된 다른 글

 

 

 

 

 

 

      

4
0
글에 대한 당신의 생각을 기다립니다. 댓글 의견 주세요!x