-
[MySQL] #1 Replication(Source/Replica) 구축과 동작 원리 이해Database 2026. 4. 11. 21:00
들어가며
서비스 규모가 커질수록 데이터베이스는 단일 인스턴스로 감당하기 어려운 한계에 직면하게 됩니다.
- 트래픽 증가로 인한 성능 저하
- 장애 발생 시 서비스 중단
- 백업 및 복구의 어려움
이러한 문제를 해결하기 위한 가장 기본적인 접근 방식이 바로 Replication(Source/Replica) 구조입니다.
이번 글에서는 MySQL의 Replication을 직접 구성하고, 데이터가 어떤 방식으로 복제되는지 그 동작 원리를 함께 확인해보겠습니다.

구조 (Topology)
Source / Replica: MySQL Replication은 서비스의 원천 데이터를 보유한 하나의 Source와 이를 실시간으로 복제하는 하나 이상의 Replica로 구성됩니다.
주요 역할
Source (Write & Logging)
- 데이터의 변경 작업(INSERT / UPDATE / DELETE) 전담 처리
- Replication의 근간이 되는 Binary Log 생성 및 관리
Replica (Read & Replay)
- Source의 Binary Log를 전달받아 데이터를 동일하게 재실행(Replay)
- 대량의 읽기 요청 처리를 통한 시스템 전체의 부하 분산
Replication을 사용하는 이유
- 데이터 보존 및 가용성(High Availability): Source 장애 발생 시 Replica를 통해 서비스를 신속히 복구하고 데이터 유실을 방지합니다.
- 부하 분산 (Read Scale-out) : 서비스 트래픽이 증가할 때, 쓰기 작업은 Source로 고정하고 읽기 작업은 여러 대의 Replica로 나누어 처리하여 병목 현상을 해결합니다.
- 성능 및 운영 효율화: 서비스 성능에 영향을 줄 수 있는 대규모 분석 쿼리나 데이터 백업 작업을 Replica에서 수행하여 운영 서버의 안정성을 확보합니다.
MySQL Replication
동작 메커니즘
1. Source에서 데이터 변경 발생
- 사용자로부터 INSERT, UPDATE, DELETE, DDL 요청이 Source 서버로 들어옵니다.
- 트랜잭션은 먼저 InnoDB Buffer Pool(메모리)에서 수행됩니다.
- 동시에 변경 내용은 Redo Log / Undo Log에 기록됩니다.
- 실제 데이터 파일 반영은 즉시가 아닌 지연(Flush 시점)될 수 있습니다.
즉, "메모리에서 처리 + 로그 기록 → 이후 디스크 반영" 구조입니다.
2. Binary Log 기록 (Source)
- 트랜잭션이 Commit 되는 시점에 변경 내용이 Binary Log(binlog)에 기록됩니다.
- Binary Log에는 '어떤 데이터가 어떻게 변했는지'에 대한 정보가 들어있으며, 복제뿐만 아니라 시점 복구(Point-in-Time Recovery)에도 사용됩니다.
- Binary Log의 특징
- 트랜잭션 단위로 원자성(Atomicity) 보장
- Replication 및 Point-in-Time Recovery(PITR)에 사용
- 기록 방식
MySQL 설정에 따라 저장 방식이 달라집니다.- Statement 기반: 실행된 SQL 저장
- Row 기반: 변경된 데이터 저장
- Mixed: 상황에 따라 자동 선택
- 내부 동작 흐름
- Prepare → Binlog 기록 → Commit
3. Replica가 Binary Log 수신 (I/O Thread)
- Replica 서버의 I/O Thread가 기동되면 Source 서버에 접속을 요청합니다.
- Source 서버는 Binlog Dump Thread를 생성하여 Binary Log의 내용을 읽어 Replica에게 네트워크를 통해 전송합니다.
- Replica는 다음 두 가지 방식 중 하나를 기준으로 데이터를 요청합니다.
- File + Position 기반 방식:
Source의 특정 Binary Log 파일과 위치(Position)를 기준으로, 마지막으로 읽은 이후의 데이터를 요청 - GTID (Global Transaction ID) 기반 방식:
각 트랜잭션에 부여된 고유 ID를 기준으로, Replica가 아직 적용하지 않은 트랜잭션만 요청
- File + Position 기반 방식:
4. Relay Log 저장 (Replica)
- 수신된 데이터는 Replica의 메모리에만 머무는 것이 아니라, 디스크 상의 Relay Log 파일에 즉시 기록됩니다.
- Relay Log는 Source의 Binary Log를 그대로 복사해온 임시 저장소입니다. I/O Thread는 오직 '로그를 가져와서 Relay Log에 쓰는 것'에만 집중하기 때문에, 네트워크 속도만 빠르다면 소스의 변경 사항을 빠르게 복사해올 수 있습니다.
5. SQL Thread의 실행 및 반영 (Replica)
- Replica는 Relay Log를 실시간으로 감시하다가, 새로운 데이터가 들어오면 이를 읽어서 Replica DB에 다시 실행(Replay)합니다. 이 과정에서 실제로 데이터가 Replica의 스토리지 엔진(InnoDB)에 반영됩니다.
과거에는 단일 SQL Thread가 모든 작업을 순차적으로 처리했기 때문에, 처리 속도가 느려질 경우 Replication Lag이 쉽게 발생했습니다.
현재(MySQL 5.7 이상)는 이를 개선하기 위해 병렬 복제(Multi-Threaded Replication)를 지원합니다.
- Coordinator Thread가 Relay Log를 읽어 트랜잭션을 분배하고
- 여러 개의 Worker Thread가 이를 병렬로 실행하여 데이터에 반영합니다.
단, 병렬 처리는 서로 충돌하지 않는 트랜잭션에 한해서만 수행됩니다.
따라서 I/O Thread는 빠르게 로그를 가져오는 반면, SQL Thread(또는 Worker Threads)는 실제 쿼리를 수행해야 하므로 상대적으로 느릴 수 있으며,
이 적용 속도 차이가 바로 Replication Lag(복제 지연)이 발생하는 주된 원인이 됩니다.Replication Lag (복제 지연)
Replication Lag 란 데이터베이스 복제 구조에서 Source서버에 반영된 변경 사항이 Replica서버에 적용되기까지 걸리는 시간 차이를 의미합니다.
Replication은 Binary Log 기반 비동기 구조(Asynchronous)로 동작합니다.
즉, Source 트랜잭션 완료 ≠ Replica 반영 완료 인 상황이 발생될 수 있고, 이로 인해 발생하는 것이 바로 Replication Lag 입니다.
1. "Source 완료 ≠ Replica 반영"
복제 지연이 발생하면 시스템은 다음과 같은 데이터 불일치 상태에 빠집니다.
- 지연 발생 전: 사용자가 게시글 작성(Source) → 목록 새로고침(Replica 조회) → 글이 보임 (정상)
- 지연 발생 시: 사용자가 게시글 작성(Source) → 목록 새로고침(Replica 조회) → 글이 안 보임 (데이터 유실로 오인)
2. 왜 지연이 발생하는가?
Source에서 트랜잭션이 완료되었다고 해서 Replica에 즉시 반영되는 것은 아닙니다. 다음과 같은 상황에서 Replication Lag이 발생합니다.
- 구조적 속도 차이: I/O Thread는 단순히 로그를 '받아서 적는' 일만 하지만, SQL Thread는 실제로 데이터를 '쓰는(Write)' 작업을 수행합니다. 쓰기 부하가 몰리면 SQL Thread의 처리 속도가 I/O Thread를 따라가지 못하게 됩니다.
- 네트워크 지연: Source와 Replica 간의 네트워크 대역폭이 좁거나 불안정할 경우 로그 전송이 늦어집니다.
- Lock 경합: Replica에서 수행 중인 다른 읽기 작업(분석 쿼리 등)이 SQL Thread의 반영 작업을 방해할 때 발생합니다.
MySQL Replication
구성 요소- Binary Log (Source) : 데이터의 모든 변경 이력이 기록되는 로그 파일
- Binlog Dump Thread (Source): Replica가 요청하면 Binary Log를 읽어 전달하는 스레드
- I/O Thread (Replica) : Source에 접속해 로그를 요청하고, 받아온 데이터를 Relay Log에 저장하는 스레드
- Relay Log (Replica) : Source로부터 전달받은 변경 사항을 실행 전 임시로 보관하는 로그 파일
- SQL Thread (Replica) : Relay Log에 기록된 내용을 읽어 실제 DB에 쿼리로 반영하는 스레드
실습 환경 구성
Docker Compose를 이용하여 다음과 같이 구성합니다.
Source / \ Replica1 Replica2- Source 1대
- Replica 2대
Replication(Source/Replica) 구축
1. Source 설정
접속
docker exec -it mysql-master mysql -uroot -prootReplication 계정 생성
MySQL 8에서는 기본 인증 방식이 caching_sha2_password이므로 Replica 연결을 위해 mysql_native_password 사용
mysql> CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'repl'; Query OK, 0 rows affected (0.02 sec) mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; Query OK, 0 rows affected (0.02 sec) mysql> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.01 sec)Binary Log 위치 확인
mysql> SHOW MASTER STATUS; +------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000003 | 827 | | | | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec)이 값이 Replica 연결의 기준이 됩니다.
2. Replica1 설정
접속
docker exec -it mysql-replica1 mysql -uroot -prootReplication 연결
Master에서 조회한 Binary Log 정보 넣기
mysql> CHANGE REPLICATION SOURCE TO SOURCE_HOST='mysql-master', SOURCE_USER='repl', SOURCE_PASSWORD='repl', SOURCE_LOG_FILE='mysql-bin.000003', SOURCE_LOG_POS=827; Query OK, 0 rows affected, 2 warnings (0.04 sec) mysql> START REPLICA;MySQL 8 환경에서는 SLAVE → REPLICA, MASTER → SOURCE 로 용어가 개선되며 CHANGE MASTER TO, MASTER_XXX, START SLAVE 와같은 이전 용어로된 명령어 사용 시 deprecated 경고가 발생할 수 있습니다.
이는 기능적인 오류가 아닌 향후 제거 예정에 대한 안내 메시지이며, Replication 동작에는 영향을 주지 않습니다.또한 비암호화 통신 및 계정 정보 저장에 대한 보안 경고가 발생하지만, 실습 환경에서는 기능적인 문제는 없으며 운영 환경에서는 별도의 보안 설정이 필요합니다.
상태 확인
Replica_IO_Running: Yes
Replica_SQL_Running: Yesmysql> SHOW REPLICA STATUS\G *************************** 1. row *************************** Replica_IO_State: Waiting for source to send event Source_Host: mysql-master Source_User: repl Source_Port: 3306 Connect_Retry: 60 Source_Log_File: mysql-bin.000003 Read_Source_Log_Pos: 827 Relay_Log_File: relay-bin.000002 Relay_Log_Pos: 326 Relay_Source_Log_File: mysql-bin.000003 Replica_IO_Running: Yes Replica_SQL_Running: Yes 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_Source_Log_Pos: 827 Relay_Log_Space: 530 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Source_SSL_Allowed: No Source_SSL_CA_File: Source_SSL_CA_Path: Source_SSL_Cert: Source_SSL_Cipher: Source_SSL_Key: Seconds_Behind_Source: 0 Source_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Source_Server_Id: 1 Source_UUID: 49f90f62-363b-11f1-9e31-720899bd14c6 Source_Info_File: mysql.slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Replica_SQL_Running_State: Replica has read all relay log; waiting for more updates Source_Retry_Count: 86400 Source_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Source_SSL_Crl: Source_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 0 Replicate_Rewrite_DB: Channel_Name: Source_TLS_Version: Source_public_key_path: Get_Source_public_key: 0 Network_Namespace: 1 row in set (0.00 sec)3. Replica2 설정
접속
docker exec -it mysql-replica2 mysql -uroot -prootReplication 연결
Master에서 조회한 Binary Log 정보 넣기
mysql> CHANGE REPLICATION SOURCE TO SOURCE_HOST='mysql-master', SOURCE_USER='repl', SOURCE_PASSWORD='repl', SOURCE_LOG_FILE='mysql-bin.000003', SOURCE_LOG_POS=827; Query OK, 0 rows affected, 2 warnings (0.04 sec) mysql> START REPLICA; Query OK, 0 rows affected (0.08 sec)상태 확인
Slave_IO_Running: Yes
Slave_SQL_Running: Yesmysql> SHOW REPLICA STATUS\G *************************** 1. row *************************** Replica_IO_State: Waiting for source to send event Source_Host: mysql-master Source_User: repl Source_Port: 3306 Connect_Retry: 60 Source_Log_File: mysql-bin.000003 Read_Source_Log_Pos: 827 Relay_Log_File: relay-bin.000002 Relay_Log_Pos: 326 Relay_Source_Log_File: mysql-bin.000003 Replica_IO_Running: Yes Replica_SQL_Running: Yes 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_Source_Log_Pos: 827 Relay_Log_Space: 530 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Source_SSL_Allowed: No Source_SSL_CA_File: Source_SSL_CA_Path: Source_SSL_Cert: Source_SSL_Cipher: Source_SSL_Key: Seconds_Behind_Source: 0 Source_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Source_Server_Id: 1 Source_UUID: 49f90f62-363b-11f1-9e31-720899bd14c6 Source_Info_File: mysql.slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Replica_SQL_Running_State: Replica has read all relay log; waiting for more updates Source_Retry_Count: 86400 Source_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Source_SSL_Crl: Source_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 0 Replicate_Rewrite_DB: Channel_Name: Source_TLS_Version: Source_public_key_path: Get_Source_public_key: 0 Network_Namespace: 1 row in set (0.01 sec)5. 복제 테스트
Source
mysql> CREATE DATABASE testdb; Query OK, 1 row affected (0.02 sec) mysql> USE testdb; Database changed mysql> CREATE TABLE t1 (id INT); Query OK, 0 rows affected (0.09 sec) mysql> INSERT INTO t1 VALUES (1); Query OK, 1 row affected (0.03 sec)Replica1, Replica2
mysql> SELECT * FROM testdb.t1; +------+ | id | +------+ | 1 | +------+ 1 row in set (0.00 sec)Source에서 입력한 데이터가 Replica까지 정상 복제된것을 확인할 수 있습니다.
'Database' 카테고리의 다른 글
[MySQL] #2 ProxySQL를 통한 DB Read/Write 분리 및 캐싱 (0) 2026.04.11 [MySQL] #0 MySQL HA + ProxySQL + 모니터링 + 자동화 구축기 (0) 2026.04.11 [mongodb] MongoDB Docker 설치 및 실습 (1) 2025.05.04 [mongodb] Python에서 MongoDB 연동, 기초 실습 (pymongo) (0) 2025.05.04 [Oracle] Predicate Information - Access, Filter (0) 2024.07.13