복제와 GTID
복제의 심장, binlog 전달
MySQL 복제는 소스 서버에서 일어난 변경을 레플리카가 동일하게 재실행하는 메커니즘이다. 읽기 부하 분산, 장애 시 빠른 서비스 복구, 무중단 스키마 변경 적용 등이 모두 복제 위에서 돌아간다.
복제의 데이터 통로는 binlog다. 소스에서 커밋된 트랜잭션이 binlog에 기록되면 레플리카가 그 파일을 읽어 같은 SQL을 재실행한다. 이 과정은 두 스레드로 나뉜다.
- I/O 스레드: 소스에 TCP로 접속해 binlog 이벤트를 받아 relay log에 저장한다.
- SQL 스레드: relay log를 순서대로 읽어 로컬 InnoDB에 적용한다.
위치 기반 복제의 문제
GTID 이전에는 레플리카가 "어디까지 받았는가"를 binlog 파일명 + 바이트 오프셋 으로 관리했다.
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='db-primary',
SOURCE_LOG_FILE='binlog.000012',
SOURCE_LOG_POS=4;단일 소스·단일 레플리카 구조에서는 문제없다. 하지만 소스가 죽어 레플리카를 새 소스로 승격할 때 문제가 생긴다. 새 소스의 binlog 파일명과 위치는 기존 소스와 다르기 때문에, 나머지 레플리카를 재연결하려면 DBA가 수동으로 "어느 파일, 어느 위치"를 계산해야 한다. 계산이 틀리면 데이터 누락이나 중복이 발생한다.
GTID: 트랜잭션마다 전역 고유 ID
GTID(Global Transaction Identifier) 는 MySQL 5.6에서 도입됐다. 클러스터 전체에서 유일한 ID를 커밋된 트랜잭션마다 부여하는 방식이다.
형식:
{source_uuid}:{transaction_id}source_uuid: 해당 트랜잭션을 최초로 커밋한 서버의server_uuid(MySQL 시작 시 생성,auto.cnf에 저장)transaction_id: 그 서버에서 단조 증가하는 숫자 (1부터 시작)
예: 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-50
이 표기는 같은 UUID를 가진 서버에서 1번부터 50번까지의 트랜잭션이 모두 포함된 GTID 집합(GTID set) 이다. 여러 서버를 묶어 표현할 수도 있다.
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-50,
7D1A9C83-AAAA-BBBB-CCCC-111111111111:1-120핵심 시스템 변수
| 변수 | 의미 |
|---|---|
gtid_mode | OFF / ON_PERMISSIVE / ON. 새 배포는 ON 권장 |
enforce_gtid_consistency | GTID와 호환되지 않는 DDL(비트랜잭션·트랜잭션 혼합 DML 등)을 거부 |
gtid_executed | 현재 서버가 커밋한 전체 GTID 집합. mysql.gtid_executed 테이블에도 영구 저장됨 |
gtid_purged | binlog에서 삭제됐지만 실행된 GTID 집합. gtid_executed의 부분집합 |
-- 현재 서버의 실행 GTID 집합 확인
SELECT @@gtid_executed;
-- 레플리카 복제 상태 확인
SHOW REPLICA STATUS\G
-- Executed_Gtid_Set: 레플리카가 완료한 GTID 집합
-- Retrieved_Gtid_Set: 소스로부터 수신한 GTID 집합GTID 기반 복제 연결
GTID가 켜진 환경에서 레플리카를 소스에 연결할 때는 파일명·위치 대신 SOURCE_AUTO_POSITION=1 한 줄이면 충분하다.
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='db-primary',
SOURCE_USER='repl',
SOURCE_PASSWORD='...',
SOURCE_AUTO_POSITION=1;
START REPLICA;내부 동작:
- 레플리카가 자신의
gtid_executed집합을 소스에 전달한다. - 소스가 자신의
gtid_executed에서 레플리카 집합을 빼 누락된 트랜잭션 목록을 계산한다. - 그 차이분만 전송한다. 파일명·위치 계산이 필요 없다.
GTID 기반 복제 흐름 다이어그램
Failover가 단순해지는 이유
소스 장애 시 Executed_Gtid_Set이 가장 큰 레플리카(가장 최신)를 새 소스로 승격하고, 나머지 레플리카를 SOURCE_AUTO_POSITION=1로 재연결하면 된다. 각 레플리카가 스스로 누락 범위를 계산해 이어받기 때문에 DBA의 위치 계산이 필요 없다.
Errant 트랜잭션 주의: 레플리카에서 직접 데이터를 수정하면 소스에는 없는 GTID가 생긴다. 이 레플리카를 나중에 새 소스로 승격하면 다른 레플리카들이 해당 GTID를 요청하지 못해 복제가 중단된다. 레플리카에서의 직접 쓰기를 피하고, 의도치 않게 발생했다면 빈 트랜잭션으로 무효화한다.
-- Errant GTID를 빈 트랜잭션으로 중화
SET GTID_NEXT='errant-server-uuid:N';
BEGIN; COMMIT;
SET GTID_NEXT='AUTOMATIC';my.cnf 설정 예시
[mysqld]
# 소스와 레플리카 모두 동일하게 적용
gtid_mode = ON
enforce_gtid_consistency = ON
log_bin = binlog
binlog_format = ROW
server_id = 1 # 각 서버마다 고유한 값 필수References
- https://dev.mysql.com/doc/refman/8.4/en/replication-gtids-concepts.html
- https://dev.mysql.com/doc/refman/8.4/en/replication-gtids-failover.html
- https://blogs.oracle.com/mysql/understanding-mysql-global-transaction-identifiers-gtids-and-their-role-in-replication
- https://severalnines.com/blog/mysql-replication-and-gtid-based-failover-deep-dive-errant-transactions/
- https://oneuptime.com/blog/post/2026-03-31-mysql-what-is-gtid-based-replication/view
- https://oneuptime.com/blog/post/2026-03-31-mysql-how-mysql-gtid-replication-works-internally/view