Semi-Synchronous Replication이란 기본적으로 비동기 방식으로 처리되는 MySQL의 리플리케이션을 XA 트랜잭션을 이용하여 준동기 방식으로 처리하는 것을 뜻한다. 준동기라는 말에서 알 수 있듯이 마스터와 슬레이브의 완전한 데이터 동기를 뜻하진 않는다. 마스터에서 기록된 바이너리 로그가 슬레이브의 릴레이 로그까지 전송되는 것만을 보장하며, 슬레이브에서 릴레이 로그가 재생되어 데이터가 완전히 반영되는 것까지는 보장하지 않는다. 동시에 같은 데이터를 가지는 것을 완전히 보장하진 않지만, 복제 데이터의 손실만큼은 막는 것이 Semisync 리플리케이션의 목적인 것이다. 하지만 이러한 목적과는 맞지 않게 5.7이전까지는 데이터 손실이 발생할 수 있는 구조적 한계를 가지고 있다. 먼저, 5.6과 5.7 각각의 Semisync 리플리케이션의 처리 방식에 대해 알아보자.
- 클라이언트로부터의 COMMIT이 요청됨
- 클라이언트 스레드(커넥션 스레드)는 XA 트랜잭션을 위해 먼전 InnoDB 엔진으로 PREPARE를 실행한다.
- 바이너리 로그를 기록한다.
- InnoDB 엔진으로 COMMIT을 실행하며 XA 트랜잭션이 완료된다.
- Binlog Dump 스레드에 의해 바이너리 로그가 슬레이브로 전송된다.
- 전송된 바이너리 로그는 슬레이브의 IO Thread에 의해 릴레이 로그로 기록된다.
- 슬레이브는 Master로 ACK를 전송한다.
7-1. 슬레이브의 SQL Thread에 의해 릴레이로그가 재생되고 적용된다.
7-2a. ACK를 마스터로 전송한다.
7-2b. ACK를 클라이언트 스레드(커넥션 스레드)로 전송한다.- 최종적으로 클라이언트에게 OK패킷을 전송한다.
- 클라이언트로부터의 COMMIT이 요청됨
- 클라이언트 스레드(커넥션 스레드)는 XA 트랜잭션을 위해 먼전 InnoDB 엔진으로 PREPARE를 실행한다.
- 바이너리 로그를 기록한다.
- Binlog Dump 스레드에 의해 바이너리 로그가 슬레이브로 전송된다.
- 전송된 바이너리 로그는 슬레이브의 IO Thread에 의해 릴레이 로그로 기록된다.
- 슬레이브는 Master로 ACK를 전송한다.
6-1. 슬레이브의 SQL Thread에 의해 릴레이로그가 재생되고 적용된다.
6-2a. ACK를 마스터로 전송한다.
6-2b. ACK를 클라이언트 스레드(커넥션 스레드)로 전송한다.- InnoDB 엔진으로 COMMIT을 실행하며 XA 트랜잭션이 완료된다.
- 최종적으로 클라이언트에게 OK패킷을 전송한다.
임의의 데이터 X 에 대하여 InnoDB의 COMMIT 완료(4번 단계)되고, 슬레이브로 바이너리 로그를 전송(5번 단계)하려는 시점에 마스터가 크래시 되어버린다면 어떻게 될까? 마스터에서는 이미 COMMIT이 완료된 상태로, 어플리케이션에서는 데이터 X를 참조할 수 있게 되어버린다. 하지만 이런 상황에서 슬레이브가 마스터로 승격된다면, 데이터 X에 대해서는 바이너리 로그를 전달받지 못했기 때문에, 슬레이브는 데이터 X를 가지지 않은 상태가 된다.
즉, 어플리케이션이 마스터에서 참조한 데이터 X는 승격된 슬레이브에서는 없는 데이터가 되어버리는 것이다. (Phantom Read)
5.7부터는 바이너리 로그를 슬레이브로 전송(4번 단계)하고, 슬레이브에서 릴레이로그(5번)기록하고, ACK를 보내기(6번)까지 InnoDB의 COMMIT이 완료되지 않는다. 이로써, 임의의 데이터 x 에 대한 Phantom Read 현상은 발생하지 않는다.
슬레이브로 바이너리 로그를 전송하고 ACK를 받기전까지, InnoDB의 Commit은 완료되지 않는다.
반대로, 슬레이브에서 릴레이로그 기록을 완료하고 ACK를 마스터로 전송하는 과정에서 마스터가 크래시되어버린다면, 마스터에는 없고 슬레이브에서만 존재하는 데이터가 생기진 않을까? 이러한 경우, sync_binlog=1, 그리고 innodb_support_xa=on으로 설정되어 있다면 마스터에서의 데이터 손실은 발생하지 않는다. XA 트랜잭션에 의해 InnoDB 엔진으로는 PREPARE가 되어 있는 상태이고, 바이너리 로그로도 fsync가 완료되어 있는 상태이기 때문이다. 이런 상태가 완료된 이후에야 슬레이브로 바이너리 로그가 전송되는데, 결국 마스터에서 데이터가 손실되는 상황은 발생하지 않는다. 그리고 마스터가 크래시 이후 재시작되면 InnoDB의 PREPARE상태의 트랜잭션은 리커버리 작업에 의해 재차 COMMIT이 완료된다.
rpl_semi_sync_master_wait_point라는 시스템 변수의 설정을 통해 semisync 리플리케이션의 동작을 제어할 수 있는데, AFTER_SYNC(default)로 lossless 방식으로 동작하게 되고, AFTER_COMMIT으로 설정하면 5.7이전 버전과 같이 동작하게 된다.