복제
MySQL 모든 변경 사항은 별도의 로그 파일에 순서대로 기록되는데 이를 **바이너리 로그(Binary Log)라고 함.
MySQL 복제는 이 바이너리 로그를 기반으로 구현됐는데, 소스 서버에서 생성된 바이너리 로그가 레플리카 서버로 전송되고 레플리카 서버에서는 해당 내용을 로컬 디스크에 저장한 뒤 자신이 가진 데이터에 반영함으로써 소스 서버와 레플리카 서버 간에 동기화가 이뤄짐.
복제 스레드
- 바이너리 로그 덤프 스레드(Binary Log Dump Thread): 소스 서버에서 바이너리 로그를 읽어 레플리카 서버로 전송하는 스레드
- I/O 스레드 : 레플리카 서버에서 소스 서버로부터 바이너리 로그를 읽어 로컬 디스크에 저장하는 스레드
- SQL 스레드 : 로컬 디스크에 저장된 바이너리 로그를 읽어 SQL을 실행하는 스레드
복제 데이터
- 릴레이 로그(Relay Log) : 레플리카 서버에서 읽어들인 바이너리 로그를 디스크에 저장해둔 파일
- 커넥션 메타데이터(Connection Metadata) : 메타데이터에는 레플리케이션 I/O 스레드에서 소스 서버에 연결할 때 사용하는 DB 계정 정보 및 현재 읽고 있는 소스 서버의 바이너리 파일명과 파일내 위치값 등이 담겨있음
- 어플라이어 메타데이터(Aplier Metadata) : 레플리케이션 SQL 스레드가 실행한 마지막 이벤트 정보가 저장된 파일
GTID 복제 방식
서버들에서 고유하도록 **각 이벤트에 부여된 식별 값을 글로벌 트랜잭션 아이디(Global Transaction ID, GTID)**라고 함.
이 GTID를 이용해서 복제하면 A 마스터 서버 B, C 슬레이브 서버가 있을 때 A 서버가 죽을 경우 B 서버가 마스터 서버로 승격하게 되지만 B 서버는 replay log를 일시적으로 담고 있기 때문에 C 서버에게 레플리케이션을 할 수 없음.
이러한 상황에서 B 서버는 A, B, C 서버가 맡고 있던 부하를 모두 떠안게 되는데, 만약 GTID를 사용하게 되면 B 서버의 GTID를 기준으로 C 서버에게 레플리케이션을 할 수 있게 되어 부하를 분산할 수 있음.
GTID란?
- 서버에서 커밋된 각 트랜잭션과 연결된 고유 식별자
- GTID는 커밋되어 바이너리 로그에 기록된 트랜잭션에 한해서만 할당
- 데이터 읽기만 수행하는 SELECT 쿼리나 혹은 sql_log_bon 설정이 비활성화돼 있는 상태에서 발생한 트랜잭션은 바이너리에 기록되지 않으므로 GTID가 할당되지 않음
동기화 방식
비동기 동기화 방식
비동기 동기화 방식은 Relay Log를 레플리카 서버에 전송하고 레플리카 서버에 제대로 전송이 되었는지 Master 서버가 확인하지 않음. 즉, 동기화에 대한 보장을 하지 않음.
이러한 특징 때문에 마스터 서버는 레플리카 서버의 상태와 관계없이 동작을 한다는 것을 알 수 있고 동기화를 할 때 서버의 성능을 높일 수 있음.
반동기 동기화 방식
기존 동기화 방식은 Slave에 Relay Log를 전송하기만 하면 나머지는 알아서 처리 될 것이라고 가정하는 방식이고 반동기 동기화 방식은 Slave에 Relay Log를 전송하고 마스터 서버에 ACK를 요청하는 방식.
좀 더 정확하게는 Relay Log를 레플리카 서버에 전송했음을 마스터 서버에게 알려주는 것이고 **레플리카 서버에 동기화 내용이 기록되었다는 것을 보장하진 않음. ** 그래서 반동기 동기화 방식이라고 칭함.
- after_commit :
- 트랜잭션 Commit: 클라이언트가 트랜잭션을 커밋 요청합니다.
- 바이너리 로그에 Flush/Commit: MySQL은 먼저 바이너리 로그에 트랜잭션 로그를 기록하고 이를 플러시하여 디스크에 영구 저장합니다.
- 스토리지 엔진 Commit: 실제 데이터가 저장되는 스토리지 엔진에서 트랜잭션을 커밋합니다.
- 바이너리 로그 덤프 및 ACK 요청 이벤트 전송: 마스터는 슬레이브에게 바이너리 로그를 전송하며, 이 이벤트에 대한 ACK(확인 응답)를 요청합니다.
- 슬레이브의 릴레이 로그에 작성: 슬레이브는 이 바이너리 로그를 받아서 릴레이 로그에 기록합니다.
- 마스터에 응답 전송: 슬레이브는 기록이 완료되었음을 마스터에게 응답합니다.
- 사용자에게 커밋 응답 전송: 마스터는 트랜잭션이 완료되었음을 클라이언트에게 응답합니다.
- after_sync :
- 트랜잭션 Commit: 클라이언트가 트랜잭션을 커밋 요청합니다.
- 바이너리 로그 Flush/Commit: MySQL은 바이너리 로그를 기록하고 플러시하여 디스크에 저장합니다.
- 바이너리 로그 덤프 및 ACK 요청 이벤트 전송: 마스터는 슬레이브에게 바이너리 로그를 전송하며, 이 이벤트에 대한 ACK를 요청합니다.
- 슬레이브 릴레이 로그에 기록: 슬레이브는 이 로그를 받아서 릴레이 로그에 기록합니다.
- 마스터에 응답 전송: 슬레이브가 마스터에게 로그 기록 완료를 응답합니다.
- 스토리지 엔진 Commit: 슬레이브로부터 로그 기록 확인을 받은 후, 마스터는 스토리지 엔진에서 트랜잭션을 커밋합니다.
- 사용자에게 커밋 응답 전송: 마스터는 트랜잭션이 완료되었음을 클라이언트에게 응답합니다.
after_commit vs after_sync
after_sync 방식에서는 바이너리 로그가 슬레이브에 기록된 후에야 마스터에서 커밋이 완료되기 때문에, 슬레이브가 바이너리 로그를 수신하지 못했거나 기록하지 못한 상태에서는 마스터도 커밋을 완료하지 않습니다.
이는 데이터 일관성을 더욱 강화하는 메커니즘으로, 팬텀 리드(마스터와 슬레이브 간의 일관성 없는 데이터 읽기)와 같은 문제가 발생하지 않도록 합니다.