테마
바이너리 로그 포맷과 동기화 방식 2
바이너리 로그 포맷과 동기화 방식에서 본 비동기 복제(asynchronous replication)는 소스(source)가 레플리카(replica)를 기다리지 않고 커밋(commit)하므로 빠르지만, 소스가 죽으면 아직 전달되지 않은 트랜잭션(transaction)을 잃는다. 이 문서는 그 유실 범위를 줄이려고 레플리카의 수신 확인을 기다리는 반동기 복제(semisynchronous replication)와, 확인을 기다리는 시점에 따라 무엇이 달라지는지를 다룬다.
반동기 복제: 받았다는 확인까지 기다리기
반동기 복제는 소스가 커밋 결과를 돌려주기 전에 적어도 한 대의 레플리카가 이벤트를 받았다는 확인을 기다리는 방식이다. 이벤트는 바이너리 로그(binary log)에 적히는 변경 기록 한 건이고, 트랜잭션 하나가 이벤트 여러 개로 기록된다. 레플리카는 이벤트를 릴레이 로그(relay log)에 쓰고 디스크에 기록한 뒤에 확인 응답(ACK, acknowledgement)을 보낸다. 적용까지 끝났다는 뜻은 아니다. 기다릴 레플리카 수는 rpl_semi_sync_source_wait_for_replica_count(기본 1)로 정한다.
MySQL 8.4에서 반동기 복제는 플러그인(plugin)으로 제공된다. 소스에는 rpl_semi_sync_source, 레플리카에는 rpl_semi_sync_replica를 설치한다. 소스·레플리카의 옛 이름인 master/slave가 이름에 들어간 옛 플러그인도 아직 있지만 폐기 예정이고, 새 플러그인과 함께 설치할 수 없다.
sql
-- 소스
INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so';
SET GLOBAL rpl_semi_sync_source_enabled = 1;
-- 레플리카 (적용 후 STOP REPLICA; START REPLICA로 다시 연결)
INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so';
SET GLOBAL rpl_semi_sync_replica_enabled = 1;주의할 점이 둘 있다.
- 타임아웃 뒤에는 조용히 비동기로 돌아간다.
rpl_semi_sync_source_timeout(기본 10000밀리초, 즉 10초) 안에 확인이 오지 않으면 소스는 기다리기를 포기하고 비동기 복제로 바꾼다. 레플리카가 따라잡으면 다시 반동기로 돌아온다. 이 구간에는 유실 가능성이 비동기와 같아지므로, 반동기가 지금 동작 중인지 알려 주는 상태 변수Rpl_semi_sync_source_status를 모니터링해야 한다. - 커밋마다 네트워크 왕복 시간이 더해진다. 그래서 가까운 서버끼리 빠른 네트워크로 연결할 때 알맞다.
AFTER_SYNC와 AFTER_COMMIT
소스가 언제 확인을 기다리는지는 rpl_semi_sync_source_wait_point가 정한다. MySQL은 커밋할 때 바이너리 로그에 먼저 기록하고, 그다음 스토리지 엔진(storage engine)에 커밋해 변경을 확정한다. 대기 지점은 이 두 단계 중 어디에서 기다리느냐다.
다이어그램 로딩…
- AFTER_SYNC(기본): 바이너리 로그를 디스크에 확실히 기록(동기화)한 뒤 확인을 기다리고, 확인을 받은 다음에 엔진에 커밋한다. 확인 전에는 다른 세션도 이 변경을 볼 수 없다. 그래서 소스가 죽어 레플리카로 장애 조치(failover)를 해도, 누군가 이미 본 데이터가 사라지는 일이 없다.
- AFTER_COMMIT: 엔진에 먼저 커밋한 뒤 확인을 기다린다. 커밋한 세션은 확인 뒤에 응답을 받지만, 다른 세션은 확인 전에 이미 그 변경을 볼 수 있다. 이 사이에 소스가 죽고 레플리카가 그 변경을 받지 못했다면, 다른 세션이 봤던 데이터가 장애 조치 뒤에 사라진다.
어느 쪽이든 장애가 난 옛 소스는 그대로 다시 소스로 쓰면 안 된다. 레플리카가 확인하지 않은 트랜잭션이 옛 소스의 바이너리 로그에 남아 있을 수 있어서 버리고 새로 구성해야 한다.
유실 범위와 복제 지연은 다른 문제다
| 방식 | 소스 장애 때 유실 |
|---|---|
| 비동기 | 레플리카가 아직 못 받은 트랜잭션은 잃는다 |
| 반동기 AFTER_COMMIT | 응답 전 구간에 다른 세션이 본 변경을 잃을 수 있다 |
| 반동기 AFTER_SYNC | 성공을 받은 트랜잭션은 잃지 않는다(레플리카 한 대 이상이 받아 둠) |
| 반동기 타임아웃 뒤 | 비동기와 같다 |
반동기 복제는 "받았다"까지만 보장한다. 레플리카가 받은 이벤트를 적용하는 속도는 그대로이므로 복제 지연(replication lag)은 여전히 생긴다. 즉 반동기 복제는 장애 때 잃는 데이터를 줄이는 장치이고, 레플리카에서 최신 데이터를 읽게 해 주는 장치는 아니다. 지연을 재고 줄이는 방법은 복제 토폴로지와 고급 설정 2에서 다룬다. 모든 서버가 합의한 뒤에 커밋하는 방식이 필요하면 그룹 복제를 본다.
면접 포인트
- "반동기 복제는 데이터 유실이 없나요?" → 레플리카가 받았다는 확인만 기다린다. 타임아웃이면 비동기로 떨어진다. AFTER_SYNC여야 다른 세션이 본 데이터가 사라지지 않는다.
- "AFTER_SYNC와 AFTER_COMMIT 차이?" → 확인을 엔진 커밋 전에 기다리느냐 후에 기다리느냐. AFTER_COMMIT은 확인 전에 다른 세션이 변경을 볼 수 있다.
- "반동기면 레플리카 읽기가 최신인가요?" → 아니다. 적용은 여전히 늦을 수 있다.
