카테고리 : MySQL/기술노트

Aurora MySQL failover 원리: writer endpoint, reader promotion, DNS TTL

Aurora MySQL의 reader 승격과 writer endpoint 갱신, DNS 캐시와 연결 풀의 상호작용을 설명하고 애플리케이션 복구 기준을 정리한다.

저자: MySQL 기술 노트 작성: 2026.11.06 약 13분 7,333자
다운로드

Aurora MySQL failover 원리: writer endpoint, reader promotion, DNS TTL

Aurora에서 새 writer가 승격되었다는 이벤트가 발생해도 애플리케이션의 쓰기는 계속 실패할 수 있다. 애플리케이션이 이전 IP를 캐시하거나, 연결 풀이 끊어진 소켓을 재사용하거나, 재시도 대기 시간이 길게 설정되어 있기 때문이다. 반대로 새 연결이 성공했다고 해서 장애 직전 요청의 커밋 여부까지 해결되는 것은 아니다.

이 장은 DB 역할 전환, 접속 대상 갱신, 연결 재수립, 업무 결과 확인을 서로 다른 복구 단계로 설명한다. 대상은 단일 리전의 일반적인 Aurora MySQL writer·reader 구성이다. Global Database의 리전 간 전환, 외부 binlog replica 승격, 다중 리전의 데이터 손실 판단은 별도 주제다.

검증 범위: AWS 공식 문서로 관리형 동작을 확인하고, SQL은 폐기용 Community MySQL 8.0에서 실행했다. 실제 Aurora 클러스터의 장애 주입, DNS 전파 시간, reader 승격, RDS Proxy 전환은 시험하지 않았다. 아래 로컬 SQL 출력은 Aurora에서 수집한 장애 관측값이 아니다.

1. 스토리지 복제와 writer 승격은 다른 계층의 동작이다

일반적인 MySQL 비동기 복제에서는 replica가 source의 binlog를 수신하고 적용한다. 승격 후보의 적용 위치와 누락 트랜잭션을 확인하는 작업이 중요하다. Aurora의 같은 클러스터 안에 있는 reader는 이 구조와 동일하지 않다. writer와 reader는 분산된 클러스터 볼륨을 공유하며, 스토리지 계층이 여러 가용 영역에 데이터를 복제한다. 인스턴스별로 계산 자원과 버퍼 캐시를 가지므로 reader의 읽기 가시성과 캐시 상태까지 모두 같다는 뜻은 아니다.

따라서 Aurora의 reader 승격을 “새 writer에 전체 데이터를 복사한 뒤 외부 binlog 적용을 마치는 작업”으로 이해하면 안 된다. 관리형 서비스가 장애 상태를 판단하고 승격 대상을 선택하며, 해당 인스턴스가 쓰기를 담당할 수 있도록 역할과 접속 경로를 바꾼다. 스토리지 가용성이 높아도 SQL을 처리할 계산 인스턴스와 연결 복구는 필요하다.

구분해야 할 보호 대상은 다음과 같다.

  • 스토리지 내구성과 가용성: 특정 저장 노드나 가용 영역 장애에 대한 데이터 보호다.
  • DB 인스턴스 가용성: writer 장애 시 서비스를 재개할 계산 자원을 확보하는 문제다.
  • 클라이언트 가용성: DNS, 소켓, 연결 풀, 인증, 재시도가 새 writer를 사용할 수 있는지의 문제다.
  • 업무 정합성: 커밋 결과가 불명확한 요청을 중복 실행하지 않고 처리 결과를 확정하는 문제다.

Aurora 내부의 역할 전환은 서비스가 관리한다. 운영자가 일반 MySQL 승격 절차를 흉내 내어 reader의 read_only를 임의로 해제하거나 외부 복제 제어 명령을 실행하는 방식으로 대체해서는 안 된다.

2. 장애 감지부터 첫 정상 커밋까지

flowchart TD
    A[writer 장애 또는 계획된 전환] --> B[관리형 복구 판단]
    B --> C[reader 승격 또는 인스턴스 복구]
    C --> D[새 writer 쓰기 준비]
    C --> E[cluster endpoint 대상 갱신]
    E --> F[클라이언트 DNS 재조회]
    G[이전 소켓 오류 또는 타임아웃] --> H[연결 풀에서 실패 연결 폐기]
    H --> F
    F --> I[새 연결과 세션 초기화]
    D --> I
    I --> J[불명확한 요청의 기존 처리 결과 확인]
    J --> K[안전한 재시도와 정상 커밋 확인]

이 그림은 관측할 책임 경계를 나타낸다. 내부 구현의 모든 단계를 표현하거나, 각 단계가 반드시 직렬로 끝난다는 의미는 아니다. 예를 들어 연결 실패 감지와 관리형 승격은 동시에 진행될 수 있다.

AWS 문서는 reader가 있는 구성에서 서비스가 보통 60초 이내, 흔히 30초 이내에 복구된다고 설명한다. 이 수치는 모든 애플리케이션의 RTO 보장값이 아니다. 문서가 설명하는 일반적인 복구 동작과 실제 서비스의 정상 응답 회복 시간은 구별해야 한다. 연결 풀의 대기열, 런타임 캐시, 재시도 정책이 뒤에 남을 수 있다.

Aurora가 모든 문제에서 반드시 다른 reader로 전환하는 것도 아니다. AWS 문서는 재시작 또는 동일 인스턴스로의 복구가 더 빠른 경로일 수 있다고 설명한다. 그러므로 “접속이 끊겼다”는 증상만으로 reader 승격이 있었다고 단정하지 말고, RDS 이벤트와 실제 writer 역할 변경을 함께 확인한다.

reader가 전혀 없으면 기존 가용 영역에서 primary 인스턴스를 재생성하는 경로가 필요할 수 있다. 스토리지의 다중 AZ 구성만으로 빠른 인스턴스 승격 후보가 자동 확보되는 것은 아니다.

3. reader promotion: 우선순위는 배치 정책이지 절대 보장이 아니다

Aurora Replica의 승격 우선순위는 0부터 15까지이며 숫자가 작을수록 우선순위가 높다. 같은 우선순위에서는 더 큰 인스턴스를 우선하고, 우선순위와 크기도 같으면 동일 tier 안에서 임의의 replica가 선택될 수 있다. 우선순위 설정 변경 자체가 즉시 failover를 일으키지는 않는다.

다만 이 규칙을 운영자가 작성한 단순 정렬 쿼리와 동일시하면 안 된다. 승격 가능 상태 등 여러 요소가 관여하며, AWS 문서는 다섯 차례의 failover 시도가 실패하면 promotion tier를 더 이상 고려하지 않는다고 명시한다. 계획된 전환에서 특정 대상을 지정하는 경우도 일반적인 자동 선택과 구분해야 한다.

승격 후보 선정에는 세 가지 질문이 필요하다.

  1. 독립된 장애 영역인가? writer와 다른 AZ의 reader를 배치해야 AZ 장애에 대한 대체 경로를 확보할 수 있다. 같은 AZ에만 reader를 늘리는 것은 동일한 보호를 제공하지 않는다.
  2. 쓰기 부하를 감당할 수 있는가? 작은 reader를 최우선으로 두면 연결은 복구되지만 CPU·메모리·연결 처리량이 부족해 정상 업무가 회복되지 않을 수 있다.
  3. 읽기 부하는 어디로 이동하는가? 승격된 reader는 이제 writer다. 남은 reader의 수와 용량을 기준으로 읽기 서비스 여유를 계산해야 한다.

평상시 대용량 분석을 수행하는 reader를 우선 후보로 두면 전환 직후 자원 경쟁이 커질 수 있다. 반대로 대기 후보를 지나치게 축소하면 비용은 줄어도 장애 시 성능 목표를 충족하기 어렵다. provisioned 인스턴스의 클래스와 Aurora Serverless v2의 용량·승격 tier 동작을 같은 고정 자원 규칙으로 일반화하지 말고 구성별로 확인한다.

4. writer endpoint가 유지하는 것은 이름이지 기존 연결이 아니다

4.1 cluster endpoint와 instance endpoint

cluster endpoint는 writer endpoint라고도 부른다. 이름은 유지되지만 failover로 새 인스턴스가 writer가 되면 그 이름이 가리키는 물리 IP가 바뀐다. 쓰기 애플리케이션은 일반적으로 이 endpoint를 사용해야 한다.

instance endpoint는 특정 DB 인스턴스를 가리킨다. 장애 전 writer의 instance endpoint를 연결 문자열에 고정하면 역할 전환 뒤에도 그 인스턴스로 연결하려 할 수 있다. 다시 살아난 이전 writer가 reader가 되었을 때 접속 자체는 성공해도 쓰기가 거부되는 상황이 발생할 수 있다. 장애 대응 중 임시로 IP나 instance endpoint를 박아 넣은 설정은 장기적인 우회책이 아니다.

DNS 이름 변경과 기존 TCP 세션은 별개다. 이미 열린 소켓은 DNS를 매 SQL마다 재조회하지 않는다. DNS의 새 응답이 확인되어도 기존 연결의 상대가 자동으로 새 writer로 이동하지 않는다. 세션 변수, 임시 테이블, 진행 중 트랜잭션도 새 서버에 자동 이전되지 않는다.

4.2 reader endpoint의 두 가지 오해

reader endpoint는 기본적으로 새 연결을 reader 사이에 분산한다. 연결 하나에서 실행하는 SELECT마다 다른 reader로 보내는 SQL 라우터가 아니다. 소수의 장기 연결을 유지하는 풀은 reader 수를 늘려도 즉시 균등 분산되지 않을 수 있다.

또한 reader endpoint는 보안상의 읽기 전용 보장 장치가 아니다. 클러스터에 reader가 없으면 writer로 연결되며 쓰기도 가능하다. 읽기 서비스의 권한은 DB 계정 권한으로 제한해야 한다. 전환 중 연결 상대와 읽기 가시성이 달라질 수 있으므로, 쓰기 직후 반드시 최신 결과가 필요한 요청은 별도의 일관성 정책을 가져야 한다.

5. DNS TTL이 짧아도 복구가 늦어지는 이유

TTL은 DNS 레코드를 캐시할 수 있는 시간에 관한 정보다. DB 승격 소요 시간도, TCP 연결의 최대 수명도, 업무 복구 시간도 아니다. 운영에서는 다음 계층을 따로 점검한다.

  • DNS 응답: 현재 조회한 resolver가 돌려주는 주소와 남은 TTL이다. 재귀 resolver의 캐시가 반영된 값일 수 있다.
  • 운영체제·로컬 resolver: 노드마다 다른 캐시 또는 DNS 전달 경로를 사용할 수 있다.
  • 언어 런타임·드라이버: Java 등 런타임의 캐시 정책과 드라이버의 주소 보관 방식이 별도로 작동할 수 있다.
  • 연결 풀: DNS 캐시가 없어도 기존 연결을 재사용하면 새 이름 조회가 일어나지 않는다.
  • 오류 감지·재시도: socket timeout, 연결 획득 timeout, backoff가 재연결 기회를 늦출 수 있다.

따라서 특정 TTL 숫자를 “그 시간만 기다리면 복구된다”는 기준으로 사용하지 않는다. 실제 endpoint의 응답과 애플리케이션 캐시 설정을 측정하고, 캐시 정책이 정상적인 주소 변경을 따라가는지 확인한다. 캐시를 무조건 끄는 조치도 DNS 요청 증가와 resolver 장애 의존성을 키우므로 부하 조건에서 검토해야 한다.

다음은 승인된 리허설에서 사용할 명령 형식이다. 실제 환경의 endpoint와 클러스터 식별자로 바꿔야 하며, 이 글에서는 AWS 계정이나 실제 endpoint에 실행하지 않았다. 출력에는 내부 주소와 리소스 식별자가 포함될 수 있으므로 접근 통제된 증거 저장소에 보관한다.

CLUSTER_ID='your-cluster-id'
WRITER_ENDPOINT='your-writer-endpoint'

aws rds describe-db-clusters \
  --db-cluster-identifier "$CLUSTER_ID" \
  --query 'DBClusters[0].{Endpoint:Endpoint,ReaderEndpoint:ReaderEndpoint,Members:DBClusterMembers[*].{Instance:DBInstanceIdentifier,Writer:IsClusterWriter,Tier:PromotionTier}}'

dig +noall +answer "$WRITER_ENDPOINT"

aws rds describe-events \
  --source-type db-cluster \
  --source-identifier "$CLUSTER_ID" \
  --duration 60

CLI의 리전·인증 컨텍스트와 필요한 조회 권한을 먼저 확인한다. 클러스터 이벤트만으로 모든 인스턴스 이벤트가 수집되는 것은 아니므로 대상 DB 인스턴스의 이벤트도 별도로 확인한다. dig가 새 IP를 보였다는 사실은 해당 resolver의 관측일 뿐이며, 애플리케이션 프로세스가 같은 값을 사용했다는 증거가 아니다.

6. SQL로 확인할 수 있는 것과 확인할 수 없는 것

6.1 현재 연결 상대와 서버의 쓰기 제한

다음 쿼리는 새 연결이 실제로 어느 서버에 도달했는지 비교할 기본 관측점이다. 하나의 연결에서 실행하고 연결 생성 시각·애플리케이션 인스턴스 식별자와 함께 기록한다.

SELECT VERSION() AS mysql_version,
       @@GLOBAL.server_uuid AS backend_uuid,
       CONNECTION_ID() AS connection_id;

SELECT @@GLOBAL.read_only AS read_only,
       @@GLOBAL.super_read_only AS super_read_only;

실행 결과(MySQL 8.0.x):

mysql> SELECT VERSION() AS mysql_version,
    ->        @@GLOBAL.server_uuid AS backend_uuid,
    ->        CONNECTION_ID() AS connection_id;

+---------------+--------------------------------------+---------------+
| mysql_version | backend_uuid                         | connection_id |
+---------------+--------------------------------------+---------------+
| 8.0.46        | 7e6a6142-d975-11f1-9802-0242ac11000e |            11 |
+---------------+--------------------------------------+---------------+
1 row in set (0.00 sec)

mysql> SELECT @@GLOBAL.read_only AS read_only,
    ->        @@GLOBAL.super_read_only AS super_read_only;

+-----------+-----------------+
| read_only | super_read_only |
+-----------+-----------------+
|         0 |               0 |
+-----------+-----------------+
1 row in set (0.00 sec)

로컬 출력의 UUID와 connection ID는 한 실행에서 나온 값으로 고정 기준이 아니다. 운영에서 장애 전후 UUID가 달라졌다면 연결 상대가 바뀌었다는 관측에는 도움이 되지만, 그것만으로 관리형 failover의 원인이나 클러스터 전체의 유일 writer 여부를 증명하지는 않는다. Aurora의 역할 판단은 관리 API·이벤트와 대상 엔진에서 제공되는 관측값을 함께 사용한다.

read_only=0 역시 업무 쓰기 성공의 충분조건이 아니다. 계정 권한, 트랜잭션의 읽기 전용 설정, SQL 오류, 자원 부족이 별도로 영향을 준다. 반대로 일반 MySQL의 read_only=1은 특권 계정까지 모두 막는 절대적인 fencing 증거가 아니다. 이 변수를 조작하는 방식으로 Aurora의 관리형 역할을 대신하지 않는다.

6.2 재접속 뒤 세션 계약

서버 역할만 확인하면 연결 복구 점검은 끝나지 않는다. 새로운 연결은 애플리케이션이 요구하는 시간대, 문자셋, 격리 수준, autocommit 정책을 다시 가져야 한다. 연결 풀의 초기화 절차와 드라이버 설정이 이 계약을 보장하는지 확인한다.

SELECT @@SESSION.autocommit AS autocommit,
       @@SESSION.transaction_isolation AS isolation_level,
       @@SESSION.transaction_read_only AS tx_read_only;

SELECT @@SESSION.time_zone AS session_time_zone,
       @@SESSION.character_set_connection AS connection_charset,
       @@SESSION.collation_connection AS connection_collation;

실행 결과(MySQL 8.0.x):

mysql> SELECT @@SESSION.autocommit AS autocommit,
    ->        @@SESSION.transaction_isolation AS isolation_level,
    ->        @@SESSION.transaction_read_only AS tx_read_only;

+------------+-----------------+--------------+
| autocommit | isolation_level | tx_read_only |
+------------+-----------------+--------------+
|          1 | REPEATABLE-READ |            0 |
+------------+-----------------+--------------+
1 row in set (0.00 sec)

mysql> SELECT @@SESSION.time_zone AS session_time_zone,
    ->        @@SESSION.character_set_connection AS connection_charset,
    ->        @@SESSION.collation_connection AS connection_collation;

+-------------------+--------------------+----------------------+
| session_time_zone | connection_charset | connection_collation |
+-------------------+--------------------+----------------------+
| SYSTEM            | latin1             | latin1_swedish_ci    |
+-------------------+--------------------+----------------------+
1 row in set (0.00 sec)

이 결과는 폐기용 서버의 접속 설정이며 운영 권장값 목록이 아니다. 예를 들어 출력의 SYSTEM은 특정 업무 시간대 설정이 보장되었다는 뜻이 아니고, client 문자셋은 서버 기본 문자셋과 다를 수 있다. 실제 서비스에서는 기대값과 비교하여 계약이 맞지 않는 연결을 업무에 투입하지 않는다.

위 두 예제는 로컬 진단 SQL의 실행 가능성만 검증한다. 실제 재접속 전후 상태 보존, Aurora 역할 전환 또는 Proxy를 경유한 backend 선택을 검증한 결과는 아니다.

7. 연결 재시도보다 먼저 정해야 할 커밋 결과 처리

장애 중 오류를 두 부류로 나눠야 한다. 아직 실행하지 않은 요청은 새 연결에서 다시 시작할 수 있지만, 실행 후 응답을 받지 못한 요청은 완료 여부가 불명확하다.

대표적인 경계가 COMMIT이다. 서버가 커밋을 완료한 직후 응답 전달 전에 연결이 끊어지면, 클라이언트는 오류를 보았어도 데이터는 이미 반영되었을 수 있다. 이때 주문 생성이나 잔액 차감을 그대로 재실행하면 이중 처리가 발생한다. “연결 오류이므로 rollback되었을 것”이라는 판단은 안전하지 않다.

권장 처리 계약은 다음과 같다.

  1. 업무 요청마다 재시도 간에도 유지되는 식별자를 부여한다.
  2. 중복 방지 기록과 업무 변경이 가능한 한 같은 트랜잭션에서 원자적으로 반영되도록 설계한다.
  3. 응답이 불명확하면 새 writer에서 기존 식별자의 처리 결과를 조회한다.
  4. 결과가 없음을 확인한 뒤에도 동시 재시도 경쟁을 고려하여 유일성 제약 등으로 중복 실행을 막는다.
  5. 같은 식별자로 다른 요청 내용이 들어오면 정상 재시도로 취급하지 않고 충돌로 처리한다.

외부 결제 호출이나 메시지 발행은 DB 트랜잭션 하나로 자동 원자화되지 않는다. 상대 시스템의 멱등성 키, outbox 같은 별도 경계 설계가 필요하다. 단순히 INSERT IGNORE로 모든 중복을 숨기는 것은 요청 결과를 확정하는 절차가 아니다.

연결 오류가 발생한 기존 트랜잭션을 새 연결에서 중간 SQL부터 이어 가는 것도 피해야 한다. 기존 snapshot과 잠금, 세션 상태가 사라졌기 때문이다. 재시도 가능한 업무 단위 전체를 다시 시작하되, 완료 여부 확인과 멱등성을 전제로 한다.

8. RDS Proxy가 바꾸는 경계와 남는 책임

RDS Proxy를 사용하면 애플리케이션이 직접 cluster endpoint의 IP 변경을 따라가는 부담을 줄일 수 있다. Proxy가 현재 writer를 추적하고 DB 측 연결을 관리하므로, writer 전환의 DNS 전파·로컬 DNS 캐시에 대한 의존성을 줄이는 경로가 된다. 이는 Proxy endpoint 자체의 연결 설정이나 네트워크 문제까지 모두 없어진다는 의미는 아니다.

AWS 문서는 failover 중 대부분의 클라이언트 연결이 유지될 수 있지만 진행 중 SQL이나 트랜잭션에 있는 연결은 취소된다고 설명한다. 따라서 Proxy를 사용해도 다음 책임은 남는다.

  • 진행 중 작업의 오류를 처리하고 커밋 결과를 확인한다.
  • 애플리케이션의 전체 요청 deadline 안에서 제한된 재시도를 수행한다.
  • 세션 pinning과 장기 트랜잭션이 연결 재사용에 미치는 영향을 관측한다.
  • DB 준비 전 요청이 누적되거나 복구 직후 재접속이 폭주하지 않도록 동시성과 backoff를 제어한다.

Proxy의 유휴 연결 보존을 트랜잭션의 투명한 재실행으로 해석하면 안 된다. 드라이버의 topology-aware 기능을 사용하는 경우에도 지원 엔진·버전, 연결 풀과의 조합, 실패 감지 방식은 해당 드라이버 문서와 실제 리허설로 확인한다.

9. 장애 리허설은 단계별 증거를 남겨야 한다

계획된 failover는 승인된 점검 시간에 수행한다. 이 글은 실제 전환 명령을 실행하지 않았으며, 조회 명령의 성공을 장애 시험의 성공으로 보고하지 않는다. 리허설에서는 공통 시각 기준과 요청 ID를 사용하고 다음 사건을 따로 기록한다.

  • 장애 시작: 제어 작업 시각과 첫 업무 오류 시각을 분리한다. 계획된 전환은 실제 AZ 장애와 감지 조건이 다르다.
  • DB 역할: 이전·새 writer의 인스턴스 식별자, 역할 변경 이벤트, 후보 상태를 기록한다.
  • 경로 갱신: 각 애플리케이션 노드의 DNS 관측값과 런타임 재조회 여부를 남긴다.
  • 연결 복구: 실패 소켓 폐기, 새 연결 생성, 연결 획득 대기 시간과 인증 실패를 수집한다.
  • 업무 회복: 첫 정상 커밋만이 아니라 일정 관측 구간의 오류율·지연·처리량을 확인한다.
  • 정합성: 장애 직전·도중 요청의 중복, 누락, 결과 불명확 상태를 대조한다.

단계의 소요 시간을 무조건 합산하면 겹쳐 진행된 시간을 중복 계산할 수 있다. 반대로 “새 writer 준비” 시각 하나만 RTO 종료점으로 잡으면 클라이언트 장애 시간을 누락한다. 서비스 RTO의 시작·종료 정의를 먼저 정하고 그 사이의 업무 가용성을 측정한다.

전환 뒤 쓰기는 정상인데 읽기 지연이 증가할 수도 있다. 승격으로 reader 한 대가 빠지고 나머지 reader에 부하가 재분배되기 때문이다. writer 연결 성공과 reader 처리 용량 회복을 각각 확인한다. 이전 writer가 reader로 복귀한 뒤 이를 즉시 다시 writer로 되돌리는 자동 조치도 피한다. 역할이 바뀐 안정 상태를 평가한 후 별도 승인 절차로 판단한다.

10. 운영 점검표

장애 이전

장애 중·이후

마무리

Aurora failover의 핵심은 reader 하나가 writer로 바뀌는 사실만이 아니다. 관리형 역할 전환과 endpoint 갱신 이후에도 클라이언트는 오래된 주소와 소켓을 버리고 세션을 다시 구성하며, 불명확한 업무 결과를 확정해야 한다. TTL은 이 과정의 한 입력이지 복구 완료를 약속하는 시간이 아니다.

다음 운영 설계에서는 연결 풀·드라이버·RDS Proxy 중 어느 계층에 복구 책임을 둘지 정하고, 실제 트래픽 조건의 리허설에서 오류율과 정합성까지 검증해야 한다. 그래야 인프라의 가용성을 애플리케이션의 가용성으로 연결할 수 있다.

참고 자료

아래 AWS 공식 문서의 동작 설명은 2026-11-06 확인 기준이다. 실제 적용 시 엔진 버전과 클러스터 구성의 제한을 다시 확인한다.