Skip to content
난이도 면접 빈출문제은행 3

복제 구조와 GTID ​

복제(replication)는 원본 서버에서 커밋(commit)된 변경을 다른 서버가 받아 똑같이 다시 적용하는 기능이다. 원본을 소스(source), 따라 적용하는 서버를 레플리카(replica)라고 부른다. 변경은 소스의 바이너리 로그(binary log)에서 출발해 레플리카의 릴레이 로그(relay log)를 거쳐 적용된다. 이 문서는 복제가 왜 필요한지와 변경이 어떤 스레드를 거쳐 전달되는지를 다룬다. 레플리카가 "어디까지 받았는지"를 바이너리 로그 파일 이름과 그 안의 위치(position)로 기억하느냐 GTID(Global Transaction Identifier, 전역 트랜잭션 식별자)로 기억하느냐는 복제 구조와 GTID 2에서 이어서 본다.

용어부터: 8.4는 source와 replica만 쓴다 ​

예전 MySQL 문서와 책에는 master/slave라는 말이 나온다. MySQL 8.0.22~8.0.26에 걸쳐 source/replica라는 새 구문과 변수 이름이 들어왔고(예: START REPLICA는 8.0.22, CHANGE REPLICATION SOURCE TO는 8.0.23), MySQL 8.4에서는 옛 구문이 제거됐다. 8.4에서 CHANGE MASTER TO, START SLAVE, SHOW SLAVE STATUS를 실행하면 문법 오류가 난다. 대신 CHANGE REPLICATION SOURCE TO, START REPLICA, SHOW REPLICA STATUS를 쓴다. 소스 쪽 명령도 SHOW MASTER STATUS는 SHOW BINARY LOG STATUS로, RESET MASTER는 RESET BINARY LOGS AND GTIDS로 바뀌었다. 다만 복제 계정에 주는 권한(privilege) 이름은 지금도 REPLICATION SLAVE이고, 일부 내부 테이블 이름에도 옛 단어가 남아 있다.

복제가 필요한 이유 ​

  • 읽기 분산: 조회 요청을 여러 레플리카로 나눠 보내면 소스 한 대가 받던 읽기 부하가 줄어든다. 쓰기는 여전히 소스 한 곳에서만 받으므로, 읽기가 많은 서비스일수록 효과가 크다.
  • 백업과 분석 분리: 백업이나 무거운 통계 쿼리를 레플리카에서 돌리면 소스의 서비스 쿼리가 영향을 덜 받는다.
  • 장애 대비: 소스가 죽으면 최신에 가까운 레플리카를 새 소스로 올리는 장애 조치(failover)로 서비스를 이어 간다. 레플리카가 없으면 백업을 복원하는 동안 서비스가 멈춘다.

복제는 백업을 대신하지 못한다. 소스에서 실수로 실행한 DELETE도 그대로 복제되어 레플리카에서도 사라지기 때문이다. 실수에 대비하려면 백업과 시점 복구(point-in-time recovery)가 따로 있어야 하고, 정해 둔 시간만큼 일부러 늦게 적용하는 지연된 복제(delayed replication)도 보조 수단이 된다(복제 토폴로지와 고급 설정 참고).

변경이 전달되는 길 ​

다이어그램 로딩…
  1. 소스는 트랜잭션(transaction)을 커밋할 때 그 변경을 바이너리 로그에 기록한다. MySQL은 SQL을 해석·실행하는 서버 층과 데이터를 저장하는 스토리지 엔진(storage engine) 층으로 나뉘는데, 바이너리 로그는 서버 층이 쓰므로 어떤 엔진을 쓰든 같은 방식으로 남는다. 바이너리 로그와 InnoDB의 리두 로그(redo log)가 커밋 여부에서 어긋나면 소스에 없는 변경이 레플리카에 생기거나 그 반대가 되므로, 둘을 2단계 커밋(two-phase commit)으로 함께 확정한다. 자세한 내용은 버퍼 풀과 로그에 있다.
  2. 레플리카가 접속하면 소스는 레플리카마다 바이너리 로그 덤프 스레드(binlog dump thread)를 하나 만들어 바이너리 로그 내용을 보낸다. 바이너리 로그에는 변경이 이벤트라는 단위로 차례로 적히고, 트랜잭션 하나는 시작·행 변경·커밋 같은 이벤트 여러 개로 이루어진다. 정해 둔 시각에 SQL을 실행하는 MySQL 이벤트(event)와는 다른 뜻이다.
  3. 레플리카에서 START REPLICA를 실행하면 리시버 스레드(receiver thread)가 소스에 접속해 이벤트를 받고, 자기 디스크의 릴레이 로그에 그대로 적는다. 매뉴얼은 이 스레드를 I/O(Input/Output, 입출력) 스레드라고도 부른다.
  4. 어플라이어 스레드(applier thread)가 릴레이 로그를 읽어 레플리카의 데이터에 적용한다. 매뉴얼의 다른 이름은 SQL 스레드다. 8.4 기본값에서는 이 일을 코디네이터 스레드(coordinator thread) 하나와 작업 스레드(worker thread) 4개가 나눠 한다. 코디네이터가 릴레이 로그를 읽어 트랜잭션을 나눠 주면 작업 스레드들이 동시에 적용한다. 이 방식을 멀티 스레드 복제(multithreaded replication)라 하며 복제 토폴로지와 고급 설정 2에서 다룬다.

받는 일과 적용하는 일을 릴레이 로그로 나눈 이유가 있다. 적용이 느려도 받기는 계속 진행되므로, 소스가 갑자기 죽어도 이미 받아 둔 변경은 레플리카에 남아 마저 적용할 수 있다. SHOW REPLICA STATUS의 Replica_IO_Running과 Replica_SQL_Running이 이 두 스레드가 살아 있는지를 보여 준다. 둘 중 하나라도 No면 복제가 멈춘 것이다.

이어지는 내용은 복제 구조와 GTID 2에서 다룬다.

면접 포인트 ​

  • "복제가 어떻게 동작하나요?" → 소스 바이너리 로그 → 덤프 스레드 → 레플리카 리시버 스레드 → 릴레이 로그 → 어플라이어 스레드.
  • "릴레이 로그는 왜 따로 있나요?" → 받기와 적용을 분리해 적용이 늦어도 받기는 계속, 소스 장애 때 받아 둔 만큼은 보존.
  • "복제가 있으면 백업이 필요 없나요?" → 실수로 지운 것도 복제된다. 백업 + 시점 복구가 따로 필요.

관련 문서