Asynchronous replication의 한계: 데이터 유실 창과 운영 리스크
MySQL 비동기 복제의 커밋·수신·적용 경계를 구분하고, 데이터 유실 창과 GTID 비교, 장애 전환 및 Aurora 운영 기준을 정리한다.
복제본이 정상적으로 동작하고 지연 지표가 낮더라도, 애플리케이션에 성공을 반환한 트랜잭션이 장애 전환 후 사라질 수 있다. 비동기 복제에서 소스의 커밋 성공과 복제본의 데이터 보유는 서로 다른 사건이기 때문이다. 이 간격을 이해하지 못하면 읽기 분산을 위한 복제본을 곧바로 무손실 장애 전환 대상으로 오인하게 된다.
이 글은 MySQL 8.0 이상에서 InnoDB와 일반적인 binary log 기반 비동기 복제를 대상으로 한다. 핵심은 복제 속도를 높이는 방법보다 어느 단계까지 확인해야 어떤 보장을 할 수 있는가에 있다. 같은 리전의 Aurora 클러스터 내부 복제와 리전 간 복제는 마지막에 별도로 구분한다.
1. 커밋 성공은 어디까지의 성공인가
InnoDB 트랜잭션의 커밋은 스토리지 엔진의 redo log와 서버 계층의 binary log가 협력하는 경로를 거친다. 내부 커밋 조정과 group commit은 로컬 엔진 상태와 binary log의 일관성·기록 비용을 다룬다. 이것을 다른 서버까지 참여하는 분산 커밋으로 해석해서는 안 된다.
일반 비동기 복제에서는 소스가 복제본의 수신 확인을 기다리지 않고 클라이언트에 성공을 반환할 수 있다. 복제본에서는 receiver가 소스의 binary log 이벤트를 받아 relay log에 기록하고, applier가 이를 실행한다. 병렬 적용 구성에서는 coordinator가 작업을 배분하고 worker들이 트랜잭션을 적용한다. 네트워크 수신과 SQL 적용은 분리되어 진행된다.
sequenceDiagram
participant C as 애플리케이션
participant S as 소스
participant R as 복제본 receiver
participant A as 복제본 applier
C->>S: 트랜잭션 및 COMMIT
S->>S: 로컬 redo 및 binary log 커밋 처리
S-->>C: 성공 응답
Note over S,R: 비동기는 복제본 수신 확인이 성공 응답의 조건이 아님
S->>R: binary log 이벤트 전송
R->>R: relay log 기록
R->>A: 적용할 트랜잭션 전달
A->>A: 데이터 변경 및 커밋
그림은 성공 응답이 원격 수신보다 먼저 도착하는 허용 가능한 순서를 보여준다. 실제 전송은 로컬 처리와 겹칠 수 있으며, 항상 그림 순서대로 직렬 실행되는 것은 아니다. 중요한 것은 응답과 원격 내구성 사이에 대기 조건이 없다는 사실이다.
단계를 다음처럼 분리하면 운영 판단이 명확해진다.
| 경계 | 확인하는 사실 | 아직 보장하지 못하는 사실 |
|---|---|---|
| 클라이언트 성공 응답 | 소스가 커밋 성공으로 처리함 | 복제본이 해당 트랜잭션을 받았는지 |
| 소스 로그의 내구성 확보 | 소스가 재시작할 때 복구할 근거를 보유함 | 소스 저장장치를 잃었을 때 원격 복구 가능성 |
| 복제본 수신·relay log 기록 | 원격 서버가 이벤트를 받음 | 전원 장애 후 로그 보존과 SQL 적용 완료 |
| 복제본 적용·커밋 | 해당 복제본에서 변경이 실행됨 | 필터·수동 변경이 있는 환경의 데이터 동일성 |
innodb_flush_log_at_trx_commit=1과 sync_binlog=1은 소스의 로컬 내구성을 위한 중요한 기준이다. 다만 binary log가 활성화되어 있어야 하고, 스토리지와 운영체제가 동기화 요청을 올바르게 수행한다는 전제가 필요하다. 이 값을 설정해도 아직 전송되지 않은 트랜잭션이 다른 서버에 생기는 것은 아니다.
2. 데이터 유실 창은 지연 시간 하나가 아니다
장애 직전 클라이언트가 성공 응답을 받은 트랜잭션 집합을 C, 장애 후 접근 가능하고 복구 절차로 보존할 수 있는 트랜잭션 집합을 D라고 하자. 다른 복구 근거를 끝내 확보하지 못하면 C 중 D에 없는 트랜잭션이 성공 응답 기준의 유실 대상이 된다. 이는 집합 관계를 설명하기 위한 정의이며, C를 MySQL의 단일 상태 변수에서 그대로 조회할 수 있다는 뜻은 아니다.
소스의 gtid_executed에는 커밋되었지만 응답이 클라이언트에 도착하지 않은 트랜잭션도 포함될 수 있다. 반대로 장애 전에 수집한 GTID 스냅샷은 수집 이후 커밋을 담지 못한다. 따라서 소스 GTID와 복제본 GTID의 차이를 구했다고 해서 곧바로 고객이 확인한 유실 건수까지 확정되는 것은 아니다. 결제·주문처럼 업무 결과가 중요한 시스템에서는 요청 식별자와 외부 처리 기록도 함께 대조해야 한다.
2.1 아직 수신하지 못한 트랜잭션
소스는 성공을 반환했지만 모든 복제본이 이벤트를 받기 전에 소스의 저장장치를 잃으면 원격 복제본만으로 해당 트랜잭션을 복구할 수 없다. 이것이 비동기 복제의 기본적인 유실 창이다. 소스가 단순히 일시 정지했고 디스크를 보존했다면 나중에 binary log를 회수할 가능성이 있으므로, 일시적 접근 불가와 영구 유실을 구별해야 한다.
2.2 수신했지만 아직 적용하지 못한 트랜잭션
복제본의 실행 집합에 없더라도 relay log에 완전한 트랜잭션이 남아 있다면 적용 후 승격할 여지가 있다. 이 구간은 즉시 데이터 유실로 분류하기보다 우선 적용 대기분으로 분류한다. 다만 수신 상태에 표시되었다는 사실만으로 동시 전원 장애 이후의 보존까지 보장하지는 않는다. relay log의 동기화 정책, 저장장치 상태, 복제 메타데이터와 복구 절차를 함께 확인해야 한다.
소스가 사라진 상황에서 복제본을 무심코 재시작하거나 relay log 복구·재초기화 절차를 적용하면, 원래 소스로부터 다시 받을 수 있다는 전제가 성립하지 않을 수 있다. 유일하게 남은 로그를 초기화하기 전에 복제본의 로그와 상태를 보존하는 것이 우선이다.
2.3 소스가 살아 있는데 다른 노드를 승격한 경우
네트워크 분할에서는 장애 감지기가 소스에 접근하지 못해도 일부 애플리케이션은 기존 소스에 계속 쓸 수 있다. 새 소스까지 쓰기를 받으면 두 개의 쓰기 이력이 갈라진다. 이는 단순한 복제 지연보다 위험하다. 동일한 주문이나 재고에 서로 다른 결정을 내린 두 이력을 자동 복제로 합칠 수 있다고 가정해서는 안 된다.
이를 막는 조치가 fencing이다. 기존 소스의 쓰기 경로를 네트워크·프록시·인스턴스 제어 등으로 확실히 차단한 뒤 새로운 쓰기 권한을 부여해야 한다. DNS 변경만으로는 기존 연결이 종료되지 않으며, 장애로 접속할 수 없는 소스에 read_only를 설정하겠다는 계획도 확실한 차단 수단이 되지 못한다.
3. 평상시 확인할 로컬 내구성과 복제 상태
아래 SQL은 MySQL 8.0 테스트 인스턴스에서 실행한 예제다. 검증 환경은 binary log와 GTID, 복제 채널을 구성하지 않은 단일 인스턴스다. 설정 조회와 Performance Schema 객체·컬럼, GTID 집합 연산을 검증했으며, 소스 장애·네트워크 분할·실제 failover는 재현하지 않았다. MySQL 8.4에 적용할 때도 대상 마이너 버전과 권한을 확인해야 한다.
3.1 내구성과 GTID 설정을 먼저 읽는다
SELECT @@GLOBAL.log_bin AS log_bin,
@@GLOBAL.innodb_flush_log_at_trx_commit AS redo_flush,
@@GLOBAL.sync_binlog AS binlog_sync,
@@GLOBAL.gtid_mode AS gtid_mode;
실행 결과(MySQL 8.0.x):
mysql> SELECT @@GLOBAL.log_bin AS log_bin,
-> @@GLOBAL.innodb_flush_log_at_trx_commit AS redo_flush,
-> @@GLOBAL.sync_binlog AS binlog_sync,
-> @@GLOBAL.gtid_mode AS gtid_mode;
+---------+------------+-------------+-----------+
| log_bin | redo_flush | binlog_sync | gtid_mode |
+---------+------------+-------------+-----------+
| 0 | 1 | 1 | OFF |
+---------+------------+-------------+-----------+
1 row in set (0.00 sec)
테스트 결과에서 log_bin=0, gtid_mode=OFF는 검증용 환경의 설정이지 운영 권장값이 아니다. 운영 소스에서는 binary log 활성화, 로그 보존 기간, GTID 사용 여부와 함께 redo·binary log의 동기화 정책을 확인한다. sync_binlog의 값만 읽고 binary log가 실제로 켜져 있다고 판단하지 않는다.
설정 변경은 단순히 값을 맞추는 작업이 아니다. 디스크 동기화 비용이 커밋 지연에 미치는 영향, group commit 효과, 장애 시 허용할 유실 범위를 함께 검토해야 한다. 성능 때문에 동기화를 완화한다면 그 결과는 비동기 전송 유실 창에 로컬 장애 유실 가능성까지 추가하는 선택이다.
3.2 receiver와 applier를 분리해서 본다
SELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER
FROM performance_schema.replication_connection_status
ORDER BY CHANNEL_NAME;
실행 결과(MySQL 8.0.x):
mysql> SELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER
-> FROM performance_schema.replication_connection_status
-> ORDER BY CHANNEL_NAME;
Empty set (0.00 sec)
복제 채널이 없는 검증 인스턴스의 Empty set은 정상적인 검증 결과다. 운영 복제본에서는 기대한 채널이 존재해야 한다. SERVICE_STATE는 연결 상태이고 LAST_ERROR_NUMBER는 마지막 오류 번호다. 과거 오류가 복구 이후에도 남을 수 있으므로, 오류 시각·메시지와 현재 진행 여부를 함께 조사한다.
SELECT CHANNEL_NAME, WORKER_ID, SERVICE_STATE, LAST_ERROR_NUMBER
FROM performance_schema.replication_applier_status_by_worker
ORDER BY CHANNEL_NAME, WORKER_ID
LIMIT 8;
실행 결과(MySQL 8.0.x):
mysql> SELECT CHANNEL_NAME, WORKER_ID, SERVICE_STATE, LAST_ERROR_NUMBER
-> FROM performance_schema.replication_applier_status_by_worker
-> ORDER BY CHANNEL_NAME, WORKER_ID
-> LIMIT 8;
Empty set (0.01 sec)
이 조회도 검증 환경에서는 빈 결과를 반환한다. 복제본에서는 worker별 상태와 오류를 읽는 출발점으로 사용하며, worker가 많은 환경에서는 LIMIT를 조정한다. 모든 worker가 보이지 않는 제한된 출력만으로 전체 채널이 정상이라고 단정하지 않는다. 병렬 적용 환경의 coordinator 오류는 별도 테이블인 replication_applier_status_by_coordinator도 확인해야 한다.
운영 현장에서는 SHOW REPLICA STATUS의 Replica_IO_Running, Replica_SQL_Running, Retrieved_Gtid_Set, Executed_Gtid_Set, 오류 필드도 함께 확인한다. 오래된 MySQL 8.0 마이너 버전에서는 명령과 필드의 명칭이 다를 수 있다. 모니터링 계정에는 필요한 조회 권한만 부여하고, 소스 접속 정보가 포함된 전체 상태 출력을 외부에 그대로 공유하지 않는다.
3.3 지연 0을 무손실 증명으로 사용하지 않는다
Seconds_Behind_Source는 applier 진행과 이벤트 시각에 기반한 지표다. receiver가 네트워크 문제로 늦게 받고 있더라도 applier가 현재 받은 이벤트를 모두 처리했다면 0으로 보일 수 있다. 소스가 마지막으로 성공 응답한 트랜잭션과의 완전한 일치를 증명하는 값이 아니다. NULL도 0으로 대체해서 경보를 해제할 값이 아니다.
최소한 다음 관측을 분리한다.
- 연결·수신: receiver 상태, 재접속, heartbeat의 최근 수신 시각, 수신 GTID 진행 여부.
- 적용: applier 오류, worker 진행, 장기 트랜잭션과 잠금 대기, 수신 집합과 실행 집합의 차이.
- 내구성: 로컬 로그 동기화 정책, 저장장치 오류, relay log 보존·복구 가능성.
- 업무: 요청 식별자별 성공 응답 기록, 중요한 쓰기의 복제본 가시성, 읽기 일관성 요구.
Heartbeat는 연결의 생존성 판단에 유용하지만 모든 업무 트랜잭션의 적용을 보증하지 않는다. 소스가 조용한 경우 GTID가 움직이지 않는 것도 정상일 수 있으므로, 단순한 무변화 경보에는 쓰기 부하와 연결 상태를 함께 넣는다.
4. GTID 집합으로 수신 대기와 적용 대기를 구분한다
GTID는 트랜잭션을 식별하고 집합의 포함 관계를 비교하는 데 유용하다. 그러나 GTID는 데이터 자체를 운반하지 않으며, 소스에서 제거된 binary log를 되살리지도 않는다.
다음 예제의 UUID와 집합은 설명을 위해 만든 입력값이다. 실제 운영 측정값이 아니며, 조회 결과는 이 입력에 대한 MySQL 함수의 실제 실행 결과다. 소스의 관측 집합은 1-10, 복제본의 수신 집합은 1-8, 적용 집합은 1-6으로 둔다.
SET @sid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
SET @source_seen = CONCAT(@sid, ':1-10');
SET @received = CONCAT(@sid, ':1-8');
SET @applied = CONCAT(@sid, ':1-6');
SELECT GTID_SUBTRACT(@source_seen, @received) AS not_received,
GTID_SUBTRACT(@received, @applied) AS not_applied;
실행 결과(MySQL 8.0.x):
mysql> SET @sid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
Query OK, 0 rows affected (0.00 sec)
mysql> SET @source_seen = CONCAT(@sid, ':1-10');
Query OK, 0 rows affected (0.00 sec)
mysql> SET @received = CONCAT(@sid, ':1-8');
Query OK, 0 rows affected (0.00 sec)
mysql> SET @applied = CONCAT(@sid, ':1-6');
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT GTID_SUBTRACT(@source_seen, @received) AS not_received,
-> GTID_SUBTRACT(@received, @applied) AS not_applied;
+-------------------------------------------+------------------------------------------+
| not_received | not_applied |
+-------------------------------------------+------------------------------------------+
| aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:9-10 | aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:7-8 |
+-------------------------------------------+------------------------------------------+
1 row in set (0.00 sec)
9-10은 관측한 소스 집합 중 복제본이 받지 못한 부분이고, 7-8은 수신했지만 실행 집합에 없는 부분이다. 전자는 살아 있는 소스나 다른 복제본 등에서 확보해야 하고, 후자는 보존된 relay log가 있다면 적용 완료를 기다릴 수 있다. 둘을 같은 복제 지연으로 묶으면, 수신 경로 복구가 필요한지 적용 병목을 풀어야 하는지 판단하기 어렵다.
실제 환경의 GTID 집합은 여러 UUID와 불연속 구간을 포함할 수 있다. 마지막 숫자나 문자열 길이를 비교하는 대신 집합 함수를 사용한다. RECEIVED_TRANSACTION_SET과 실행 집합의 차이는 상태를 수집하는 동안에도 바뀔 수 있다. 필터, 다중 소스, 수동 주입 이력이 있는 환경에서는 복제 범위와 GTID 의미도 확인해야 한다.
4.1 승격 후보는 가장 큰 숫자가 아니라 포함 관계로 비교한다
SET @sid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
SET @candidate_a = CONCAT(@sid, ':1-8');
SET @candidate_b = CONCAT(@sid, ':1-7:9');
SELECT GTID_SUBSET(@candidate_a, @candidate_b) AS a_in_b,
GTID_SUBSET(@candidate_b, @candidate_a) AS b_in_a;
SELECT GTID_SUBTRACT(@candidate_a, @candidate_b) AS only_a,
GTID_SUBTRACT(@candidate_b, @candidate_a) AS only_b;
실행 결과(MySQL 8.0.x):
mysql> SET @sid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
Query OK, 0 rows affected (0.00 sec)
mysql> SET @candidate_a = CONCAT(@sid, ':1-8');
Query OK, 0 rows affected (0.00 sec)
mysql> SET @candidate_b = CONCAT(@sid, ':1-7:9');
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT GTID_SUBSET(@candidate_a, @candidate_b) AS a_in_b,
-> GTID_SUBSET(@candidate_b, @candidate_a) AS b_in_a;
+--------+--------+
| a_in_b | b_in_a |
+--------+--------+
| 0 | 0 |
+--------+--------+
1 row in set (0.00 sec)
mysql> SELECT GTID_SUBTRACT(@candidate_a, @candidate_b) AS only_a,
-> GTID_SUBTRACT(@candidate_b, @candidate_a) AS only_b;
+----------------------------------------+----------------------------------------+
| only_a | only_b |
+----------------------------------------+----------------------------------------+
| aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:8 | aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:9 |
+----------------------------------------+----------------------------------------+
1 row in set (0.00 sec)
B에 더 큰 번호인 9가 있어도 A가 가진 8이 없다. 두 집합 모두 상대의 부분집합이 아니므로 단순히 B가 더 최신이라고 결론 내릴 수 없다. 실제 병렬 적용 중에는 일시적으로 빈 구간이 관측될 수 있으며, 별도 쓰기나 이력 분기가 원인일 수도 있다. 먼저 수신 로그를 안전하게 적용할 수 있는지 확인하고, 비교 시점과 채널 범위를 맞춘다.
GTID가 같다고 테이블 내용까지 반드시 같지는 않다. 복제 필터로 변경을 제외했거나 GTID를 수동으로 처리한 이력이 있으면 실행 집합은 같아도 데이터는 다를 수 있다. 승격 후보 선정에는 집합 포함 관계뿐 아니라 필터 일치, errant transaction 여부, 데이터 검증 이력도 필요하다. 여기서 errant transaction은 기준 소스의 이력에는 없지만 복제본에서 독자적으로 실행된 트랜잭션을 뜻한다.
5. 장애 전환은 유실 판단과 쓰기 권한 이전을 함께 다룬다
비동기 복제에서는 장애 전환 속도만 최적화할 수 없다. 적용을 더 기다리거나 로그를 회수하면 복구 시점 목표인 RPO를 개선할 여지가 있지만, 서비스 복구 시간인 RTO는 늘어날 수 있다. 반대로 즉시 승격하면 서비스는 빨리 열 수 있어도 복구 가능한 트랜잭션을 누락한 채 새로운 쓰기 이력을 만들 수 있다.
계획된 전환
소스가 정상일 때는 쓰기 유입을 차단하고 진행 중인 트랜잭션을 정리한 뒤, 최종 커밋 경계를 기록한다. 후보가 그 경계를 모두 실행했는지 확인하고 나서 쓰기 권한을 이전한다. GTID 환경에서는 최종 집합을 기준으로 대기·포함 관계를 확인할 수 있다. 후보가 따라잡은 뒤에도 소스에 새 쓰기가 허용된다면 확인한 경계가 곧바로 낡아지므로, 쓰기 차단과 경계 확정의 순서가 중요하다.
비계획 전환
- 기존 소스를 격리한다. 장애 감지와 fencing을 구분한다. 기존 연결, 배치 작업, 관리자 경로까지 포함하여 이중 쓰기를 방지한다.
- 증거를 보존한다. 마지막 소스 상태, 각 후보의 수신·실행 GTID, 오류, relay log와 binary log의 보존 가능성을 수집한다. 상태 초기화를 서두르지 않는다.
- 후보를 비교한다. 살아 있는 후보들의 집합 관계와 복제 필터를 확인한다. 지연 시간이 가장 작다는 이유만으로 선택하지 않는다.
- 수신한 로그를 최대한 적용한다. 적용 오류와 미완료 트랜잭션을 조사한다. 장애 중이라는 이유로 오류 트랜잭션을 무조건 건너뛰지 않는다.
- 불확실성을 승인받는다. 최종 소스 집합을 모르면 무손실을 입증할 수 없음을 명시한다. 정해진 RPO·RTO 정책과 업무 책임자의 승인 기준에 따라 진행한다.
- 쓰기 경로를 단일화한다. 새 소스로 접속을 전환하고 커넥션 풀을 갱신한다. 승격 직후의 업무 쓰기·읽기를 검증한다.
- 기존 소스는 비교 후 재가입시킨다. 회복했다고 바로 복제 토폴로지에 되돌리지 않는다. 새 소스에 없는 이력을 보존하고 대조한 다음, 필요하면 재구축한다.
클라이언트가 COMMIT 응답을 받지 못했다면 트랜잭션이 실패했다고 단정할 수 없다. 소스에서 커밋한 뒤 응답만 유실되었을 수 있다. 요청 식별자를 통한 멱등성, 중복 방지 제약, 처리 결과 조회 경로를 마련해야 한다. GTID는 업무 요청 식별자를 대신하지 않는다.
6. 유실 창을 줄이는 선택과 그 대가
| 선택 | 기대 효과 | 남는 한계와 비용 |
|---|---|---|
| 네트워크·receiver·applier 병목 완화 | 평상시 전송·적용 지연 감소 | 성공 응답 조건은 비동기 그대로이며 최악의 유실 상한을 보장하지 않음 |
| 로컬 로그 동기화 강화 | 소스·복제본의 장애 후 복구 가능성 개선 | 원격 미수신 구간은 남고 커밋 I/O 비용 증가 가능 |
| Semisynchronous replication | 설정된 수의 복제본이 수신·기록을 확인한 뒤 성공 응답하도록 대기 | ACK는 적용 완료가 아니며 timeout 후 비동기 전환, ACK 복제본 생존·선정 문제를 관리해야 함 |
| 읽기 경로의 인과성 확인 | 성공한 쓰기를 복제본에서 읽기 전에 적용 여부 확인 | 읽기 대기·timeout 처리가 필요하며 그 자체로 커밋 내구성이 강화되지는 않음 |
| binary log 보관·백업·복구 훈련 | 잃어버린 이력을 회수하거나 특정 시점으로 복구할 수 있는 근거 확보 | 전송·보관 전 사라진 구간은 복구할 수 없고 복구 시간도 필요 |
Semisynchronous replication의 ACK는 복제본이 이벤트를 relay log에 기록하고 디스크로 flush한 뒤 보내는 확인이다. ACK를 받은 복제본이 실제 승격 후보와 같은지, wait point와 대기 복제본 수가 무엇인지, timeout으로 비동기 모드가 된 시간이 있었는지를 함께 운영해야 한다. 기능을 설치했다는 사실만으로 모든 장애에서 무손실이라고 표현하지 않는다.
읽기 일관성도 내구성과 별개다. 쓰기 직후 복제본에서 읽으면 아직 적용되지 않은 결과를 볼 수 있다. 중요한 읽기를 소스로 보내거나, 애플리케이션이 해당 쓰기의 GTID를 전달해 대상 복제본에서 WAIT_FOR_EXECUTED_GTID_SET으로 제한 시간 동안 기다리는 방식 등을 설계할 수 있다. 대기 실패 시에는 오류·소스 읽기 전환 등 명시적인 정책이 필요하며, 기존 REPEATABLE READ 스냅샷을 그대로 사용하면 적용 완료 후에도 옛 데이터를 볼 수 있다는 점에 유의한다.
더 강한 가용성·일관성 구성을 검토할 때는 Group Replication 같은 합의 기반 구성의 장애 모델과 커밋·읽기 보장도 별도로 평가한다. 제품이나 토폴로지 이름만으로 모든 응답·읽기·내구성 경계가 같아지는 것은 아니다.
7. Aurora MySQL에서는 복제의 범위를 먼저 구분한다
동일 Aurora DB 클러스터 내부: writer와 Aurora Replica는 클러스터 볼륨을 공유한다. 일반 MySQL 복제본처럼 각 노드가 독립 데이터 파일에 binary log를 적용하여 전체 사본을 유지하는 모델과 다르다. reader 지연은 관측될 수 있지만, 이를 곧바로 독립 복제본의 미수신 binary log 유실 창과 동일시해서는 안 된다. 클러스터 스토리지 내구성, 인스턴스 failover, reader 가시성과 연결 재설정을 나누어 판단한다.
외부 MySQL 또는 별도 클러스터와의 binary log 복제: 일반적인 비동기 전송·적용 경계가 다시 중요해진다. binlog 활성화와 보존, GTID 호환성, 필터, 관리형 서비스의 권한·프로시저 제약을 확인한다. 이 글의 일반 MySQL 관리 절차를 Aurora에 그대로 실행하지 않는다.
Aurora Global Database의 리전 간 복제: 스토리지 기반 복제이지만 리전 간 전파는 비동기다. 계획된 switchover와 비계획 failover를 구분해야 하며, 비계획 전환에서는 보조 리전에 도달하지 않은 데이터가 유실될 수 있다. AWS 문서도 failover 시 유실량이 리전 간 복제 지연에 영향을 받는다고 설명한다. 지연의 전형적인 수준을 RPO 상한 보장으로 바꾸어 말해서는 안 된다.
따라서 운영 문서에는 단순히 “Aurora 복제 지연”이라고 쓰지 말고, 동일 클러스터 reader인지, binary log 복제본인지, Global Database의 보조 리전인지 명시한다. 관측 지표와 장애 전환 판단의 단위가 달라진다.
8. 운영 점검표
마무리
비동기 복제는 소스의 쓰기 경로를 원격 응답과 분리하여 지연과 결합도를 줄인다. 그 대가로 성공 응답과 원격 데이터 보존 사이에 간격이 생긴다. 운영자는 이 간격을 낮추는 것과 없앴다고 증명하는 것을 구분해야 한다.
장애 전환의 기준은 “복제본이 살아 있다”가 아니라, 어떤 트랜잭션이 남아 있고 무엇을 더 복구할 수 있으며 누가 유일한 쓰기 권한을 갖는가이다. 이 경계를 이해하면 Semisynchronous replication의 ACK, GTID 기반 후보 선정, 복구 가능한 로그 보관 정책을 각각의 보장 범위에 맞게 설계할 수 있다.