카테고리 : MySQL/기술노트

VIP, DNS, Proxy 기반 failover 비교: 전환 시간과 캐시 문제

MySQL failover에서 VIP·DNS·Proxy의 전환 경로와 캐시, 연결 풀, 트랜잭션 재시도를 구분하고 업무 복구 시간을 검증하는 방법을 정리한다.

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

DBA가 새 primary의 승격을 확인했는데 애플리케이션은 계속 이전 서버로 접속하는 경우가 있다. 반대로 새 주소로 TCP 연결은 성공하지만 쓰기는 거부되거나, 연결 풀에 남은 오래된 연결에서만 오류가 반복되기도 한다. 세 현상은 모두 사용자에게 장애로 보이지만 원인과 해결 계층은 다르다.

VIP, DNS, Proxy는 클라이언트를 어느 서버에 연결할지 결정하는 경로다. 이 경로를 바꾸는 기능과, 최신 데이터를 가진 서버를 안전하게 writer로 만드는 기능은 같지 않다. 전환 시간을 비교하려면 주소 변경뿐 아니라 이전 writer 차단, 새 writer 준비, 기존 연결 처리, 재시도한 업무의 성공까지 함께 보아야 한다.

이 글은 MySQL 8.0 이상과 8.4 LTS 운영을 기준으로 세 경로를 비교한다. SQL은 폐기용 Community MySQL에서 실행했다. VIP 이동, 실제 DNS 캐시 만료, Proxy 장애 전환, Aurora 승격은 실험하지 않았다. 아래 시간 계산은 명시한 가정 입력의 계산 검증이며 제품 성능 측정값이나 복구 시간 보장값이 아니다.

1. 주소 전환보다 먼저 필요한 안전 조건

단일 writer 환경의 failover에는 서로 다른 네 가지 책임이 있다.

  1. 장애 판정: 서버가 멈췄는지, 일부 네트워크에서만 보이지 않는지 구분한다.
  2. 쓰기 권한 확정: 이전 writer를 차단하고 승격 후보의 데이터 상태를 확인한다.
  3. 접속 경로 갱신: 새 연결이 승인된 writer에 도착하도록 VIP, DNS 또는 Proxy 설정을 바꾼다.
  4. 업무 복구: 연결을 다시 만들고 세션을 초기화한 뒤 안전하게 업무를 재시도한다.

단순한 TCP 포트 확인이나 SELECT 1 성공은 두 번째 조건을 증명하지 못한다. 복제에서 뒤처진 서버도 SELECT를 실행할 수 있고, 네트워크 파티션 반대편의 이전 writer는 계속 쓰기를 받을 수 있다. 비동기 복제에서는 승격 후보의 적용 위치와 승인된 데이터 손실 범위를 별도로 확인해야 한다.

fencing은 이전 writer가 더 이상 업무 쓰기를 완료하지 못하게 만드는 절차다. 접근 경로 차단, 프로세스·인스턴스 격리, 검증된 클러스터 권한 제어 등이 구현 수단이 될 수 있다. super_read_only는 유용한 방어 장치지만, 장애 때문에 해당 서버에 명령을 전달할 수 없는 경우의 fencing을 대신하지 못한다. 주소만 바꾸고 이전 writer에 직접 연결할 수 있는 경로를 남기면 split-brain 위험도 남는다.

flowchart TD
    A[장애 또는 계획 전환 감지] --> B[이전 writer 차단과 후보 데이터 검증]
    B --> C{새 writer 쓰기 준비 확인}
    C -->|실패| D[전환 보류와 원인 확인]
    C -->|성공| E[VIP 소유권 또는 DNS 응답 또는 Proxy 경로 갱신]
    E --> F[오래된 연결 폐기와 새 연결 생성]
    F --> G[인증과 세션 초기화]
    G --> H[멱등성 기준에 따라 업무 재시도]
    H --> I[쓰기와 읽기 결과 및 중복 여부 확인]

이 흐름은 책임을 설명한 것이며 모든 작업이 반드시 직렬로 실행된다는 뜻은 아니다. 장애 탐지와 클라이언트의 소켓 오류 감지는 동시에 진행될 수 있다. 시간을 계산할 때는 이 중첩을 구별해야 한다.

2. VIP: 주소는 유지하고 패킷의 목적지를 옮긴다

VIP 방식에서는 애플리케이션이 같은 가상 IP로 접속하고, 장애 전환 시 그 주소를 담당하는 서버 또는 네트워크 경로가 바뀐다. 같은 L2 네트워크에서 VRRP와 주소 소유권을 사용하는 구성이 대표적이지만, 모든 VIP 구현이 이 형태인 것은 아니다. 클라우드에서는 주소 연결이나 라우팅을 제어하는 API가 별도로 필요할 수 있다.

실제로 바뀌는 것은 무엇인가

같은 L2의 IPv4 VIP가 다른 호스트로 이동하면 주변 장비가 IP와 MAC 주소의 대응 관계를 갱신해야 한다. 이때 gratuitous ARP가 쓰일 수 있다. IPv6에서는 Neighbor Discovery 계층을 고려한다. 따라서 VIP 전환에도 네트워크 캐시와 전달 경로의 수렴 시간이 있다. 주소를 올리는 명령의 성공 시각과 모든 클라이언트에서 도달 가능한 시각은 다를 수 있다.

또한 운영체제와 mysqld의 수신 주소 설정이 VIP 사용 방식과 맞아야 한다. VIP가 없는 상태에서 특정 주소로만 바인딩하도록 구성하면 기동이나 대기 노드 준비가 실패할 수 있다. 반대로 모든 인터페이스에 열어 두면 방화벽과 직접 접속 차단 정책이 중요해진다.

VIP가 보존하지 않는 것

동일한 IP를 사용해도 이전 서버의 TCP 연결 상태, MySQL 세션, 임시 테이블, 미완료 트랜잭션은 새 서버로 옮겨지지 않는다. 기존 소켓은 오류로 종료되거나 타임아웃을 기다릴 수 있다. 새 연결이 성공한다고 이미 빌려 간 풀 연결까지 정상화되는 것은 아니다.

장점은 정상적인 신규 접속 경로에서 DB 목적지 DNS 변경을 기다릴 필요가 없다는 점이다. 다만 VIP 이름 자체가 DNS로 해석되거나 VIP 앞에 다른 계층이 있다면 그 계층의 의존성은 남는다. 단점은 네트워크 토폴로지와 제어 권한의 제약이다. 온프레미스의 같은 서브넷에서 검증한 방법을 다른 AZ나 클라우드 네트워크에 그대로 적용할 수 없다.

운영 기준: VIP 소유권의 단일성, 이전 writer의 차단, 양방향 네트워크 경로, 실제 애플리케이션 위치에서의 새 접속을 각각 확인한다. VIP가 보인다는 사실을 writer 역할의 증거로 사용하지 않는다.

3. DNS: 이름은 유지하지만 해석 결과가 바뀐다

DNS 방식은 안정된 이름의 응답을 새 writer 주소로 바꾼다. 애플리케이션 설정을 바꾸지 않고 대상 서버를 교체할 수 있지만, 권한 DNS의 변경은 모든 프로세스에 즉시 전달되는 통지가 아니다.

캐시는 한 곳에만 있지 않다

운영 경로에는 재귀 resolver, 로컬 resolver나 캐시 서비스, 언어 런타임, 드라이버 등이 관여할 수 있다. 모든 계층이 독립 캐시를 갖는 것은 아니지만, 어느 계층이 실제 해석 결과를 보관하는지는 확인해야 한다. CNAME을 사용하는 구성은 각 레코드의 캐시 상태도 살펴야 한다.

TTL은 DNS 데이터의 캐시 수명을 제어하는 값이다. 그러나 TTL을 낮췄다고 이미 생성된 TCP 연결의 수명이 짧아지지는 않는다. 애플리케이션이 시작할 때 한 번 해석한 IP를 별도로 저장한다면, resolver에서 TTL이 만료되어도 새 이름 조회가 발생하지 않을 수 있다. 런타임의 캐시 정책, 실패 응답의 캐시 정책, resolver의 stale 응답 정책도 실제 환경에서 확인해야 한다.

계획 전환 전에 TTL을 낮출 때는 이전의 긴 TTL로 저장된 응답이 먼저 만료될 시간을 확보한다. 전환 순간 TTL만 낮추면 기존 캐시가 새 값을 소급 적용하지는 않는다. 장애 때 TTL을 0으로 바꾸는 것도 이미 존재하는 소켓이나 애플리케이션의 자체 캐시를 없애지 못한다.

DNS가 정상인데도 장애가 지속되는 경로

  • 운영자 단말의 조회는 새 주소지만 애플리케이션 프로세스는 이전 주소를 보관하고 있다.
  • 새 연결은 새 writer로 향하지만 풀에서 재사용하는 연결은 이전 서버에 남아 있다.
  • 이전 writer가 reader로 내려간 뒤에도 읽기 질의가 성공하여 단순 연결 검증을 통과한다.
  • 잘못된 이름이나 일시적 응답 실패가 캐시되어 정상화 뒤에도 접속이 지연된다.

dig 같은 조회는 실행한 위치의 DNS 경로만 확인한다. 그것만으로 JVM, 별도 컨테이너, 다른 네트워크의 프로세스가 사용하는 값을 증명할 수 없다. 애플리케이션에서 실제로 선택한 목적지 IP와 새 연결의 시각을 관측해야 한다.

운영 기준: 이름 해석 시간, 실제 접속 주소, 풀 연결 생성 시각, backend 식별자를 함께 기록한다. TTL을 서비스 RTO와 동일시하지 말고 오류 연결의 폐기 및 재시도 정책까지 검증한다.

4. Proxy: 접속 입구와 backend 선택을 분리한다

Proxy는 클라이언트 연결을 받은 뒤 backend를 선택한다. 이 구조는 writer 선택 책임을 집중시키고 클라이언트가 DB 노드 주소를 직접 관리하는 부담을 줄인다. 그러나 DB 장애 전환에서 제거한 복잡성의 일부가 Proxy의 가용성, 역할 감지, 상태 갱신, 연결 관리로 이동한다.

일반 TCP 중계와 프로토콜 인식형을 구별한다

일반 TCP 중계는 통상 연결 단위로 backend를 정한다. backend가 실패하면 그 위의 MySQL 세션과 진행 중인 작업을 투명하게 다른 서버로 옮길 수 없다. 새 연결을 다른 backend에 붙이는 것과 기존 트랜잭션을 이어 주는 것은 별개다.

MySQL 프로토콜을 이해하는 제품은 세션 상태를 추적하고 트랜잭션 경계에서 backend 연결을 재사용할 수 있다. 그렇더라도 지원하는 상태와 제약이 있으며, session pinning이 발생하면 다중화 범위가 줄어든다. 일반 TCP Proxy의 동작을 모든 DB Proxy에 일반화해서도, 일부 관리형 Proxy의 연결 보존 기능을 모든 Proxy에 기대해서도 안 된다.

MySQL Router의 기본 연결 라우팅은 backend 장애에 따른 연결 오류를 애플리케이션이 처리하고 재접속하는 모델로 이해한다. Router 8.4에는 별도 조건을 갖춘 connection sharing 및 읽기·쓰기 분리 기능도 있으므로, 기본 라우팅 설명을 근거로 “Router는 어떤 설정에서도 SQL과 세션을 이해하지 않는다”고 단정하지 않는다.

건강 상태보다 역할이 중요하다

Proxy가 대상 포트가 열렸는지만 확인하면 살아 있는 reader로 쓰기 연결을 보낼 수 있다. writer 역할뿐 아니라 클러스터가 승인한 대상인지 확인하는 신뢰 가능한 정보원이 필요하다. 정적 backend 목록, 역할 조회, 클러스터 메타데이터 등 어떤 방식을 사용하는지와 상태 갱신 주기를 문서화한다.

Proxy 자체도 단일 장애점이 될 수 있다. 애플리케이션별 로컬 Router 구성과 중앙 Proxy 이중화 구성은 서로 다른 장애 범위를 가진다. 중앙 Proxy 앞에 VIP나 DNS를 두는 경우에는 세 방식이 경쟁 관계가 아니라 겹친 계층이 된다. Proxy의 장애와 DB writer의 장애를 별도 시험으로 나누어야 한다.

운영 기준: Proxy 생존, backend 역할 판단, stale backend 제거, front-end 연결 유지 여부, backend 연결 수와 대기열을 따로 관측한다. 전환 중 과도한 재접속이 인증·TLS 처리와 DB 연결 한도를 소진하지 않도록 제한한다.

5. 전환 시간은 절차의 합보다 마지막 준비 시점으로 본다

장애 발생을 기준 시각 0으로 두고, 다음 세 완료 시각을 기록한다.

  • writer_ready_s: 이전 writer의 차단과 데이터 검증을 마친 새 writer가 쓰기를 받을 준비가 된 시각.
  • route_ready_s: 관측 대상 클라이언트에서 새 연결이 올바른 경로로 갈 수 있게 된 시각.
  • client_retry_ready_s: 해당 클라이언트가 오래된 연결의 실패를 인식하고 재시도할 준비가 된 시각.

모든 준비가 끝난 직후 접속을 시작할 수 있다는 단순 모델에서는 세 값의 최댓값에 연결·인증·세션 초기화·업무 확인 시간을 더한다. 이 모델은 재시도 간격 때문에 준비 시점을 놓치는 경우, DNS를 다시 조회하지 않는 경우, 대기열과 재시도 폭주를 포함하지 않는다. 따라서 관측되지 않은 항목을 0으로 채워 복구 완료를 선언하면 안 된다.

아래 입력은 설명용 가정이다. route_fast는 경로가 일찍 준비된 경우, dns_ttl_wait와 dns_app_cache는 각각 resolver와 애플리케이션 캐시가 경로 준비를 늦춘 경우를 표현한다. 특정 제품의 속도 순위를 뜻하지 않는다. unobserved는 경로 준비를 측정하지 못한 경우다. 시간 단위는 모두 초다.

WITH assumptions AS (
    SELECT 'route_fast' AS scenario, 20 AS writer_ready_s,
           6 AS route_ready_s, 22 AS client_retry_ready_s,
           3 AS connect_and_check_s, 60 AS budget_s
    UNION ALL
    SELECT 'dns_ttl_wait', 20, 45, 22, 3, 60
    UNION ALL
    SELECT 'dns_app_cache', 20, 120, 22, 3, 60
    UNION ALL
    SELECT 'unobserved', 20, NULL, 22, 3, 60
), estimate AS (
    SELECT scenario, budget_s,
           GREATEST(writer_ready_s, route_ready_s,
                    client_retry_ready_s) + connect_and_check_s AS model_recovery_s
    FROM assumptions
)
SELECT scenario, model_recovery_s, budget_s,
       CASE
           WHEN model_recovery_s IS NULL THEN 'INSUFFICIENT_EVIDENCE'
           WHEN model_recovery_s <= budget_s THEN 'WITHIN_MODEL_BUDGET'
           ELSE 'OVER_MODEL_BUDGET'
       END AS assessment
FROM estimate
ORDER BY scenario;

실행 결과(MySQL 8.0.x):

mysql> WITH assumptions AS (
    ->     SELECT 'route_fast' AS scenario, 20 AS writer_ready_s,
    ->            6 AS route_ready_s, 22 AS client_retry_ready_s,
    ->            3 AS connect_and_check_s, 60 AS budget_s
    ->     UNION ALL
    ->     SELECT 'dns_ttl_wait', 20, 45, 22, 3, 60
    ->     UNION ALL
    ->     SELECT 'dns_app_cache', 20, 120, 22, 3, 60
    ->     UNION ALL
    ->     SELECT 'unobserved', 20, NULL, 22, 3, 60
    -> ), estimate AS (
    ->     SELECT scenario, budget_s,
    ->            GREATEST(writer_ready_s, route_ready_s,
    ->                     client_retry_ready_s) + connect_and_check_s AS model_recovery_s
    ->     FROM assumptions
    -> )
    -> SELECT scenario, model_recovery_s, budget_s,
    ->        CASE
    ->            WHEN model_recovery_s IS NULL THEN 'INSUFFICIENT_EVIDENCE'
    ->            WHEN model_recovery_s <= budget_s THEN 'WITHIN_MODEL_BUDGET'
    ->            ELSE 'OVER_MODEL_BUDGET'
    ->        END AS assessment
    -> FROM estimate
    -> ORDER BY scenario;

+---------------+------------------+----------+-----------------------+
| scenario      | model_recovery_s | budget_s | assessment            |
+---------------+------------------+----------+-----------------------+
| dns_app_cache |              123 |       60 | OVER_MODEL_BUDGET     |
| dns_ttl_wait  |               48 |       60 | WITHIN_MODEL_BUDGET   |
| route_fast    |               25 |       60 | WITHIN_MODEL_BUDGET   |
| unobserved    |             NULL |       60 | INSUFFICIENT_EVIDENCE |
+---------------+------------------+----------+-----------------------+
4 rows in set (0.00 sec)

WITHIN_MODEL_BUDGET는 주어진 모델의 계산 결과일 뿐 실제 운영 승인 상태가 아니다. 경로가 일찍 준비되어도 writer나 클라이언트가 늦으면 복구는 빨라지지 않는다. DNS TTL 단축만 반복하기 전에 어느 완료 시각이 가장 늦는지 확인해야 하는 이유다.

실제 시험에서는 첫 성공 요청만 기록하지 않는다. 오류율이 정상 범위로 돌아오는 시각, 연결 풀의 오래된 연결이 소진되는 시각, 요청 지연의 상위 백분위수, 중복 쓰기 및 누락 건수를 함께 측정한다. 일부 인스턴스만 정상인 상태를 전체 서비스 복구로 보고하지 않는다.

6. 새 연결이 어느 서버에 도착했는지 확인한다

다음 쿼리는 실행한 연결의 backend 식별자, 연결 식별자, 읽기 전용 플래그와 세션 설정을 보여 준다. 복구 리허설에서는 실제 애플리케이션과 같은 접속 경로로 새 물리 연결을 생성한 뒤 확인한다. 아래 출력 자체는 Proxy가 없는 단일 테스트 서버에서 얻었다.

SELECT VERSION() AS mysql_version,
       @@GLOBAL.server_uuid AS backend_uuid,
       CONNECTION_ID() AS connection_id,
       @@GLOBAL.read_only AS read_only,
       @@GLOBAL.super_read_only AS super_read_only,
       @@SESSION.autocommit AS autocommit,
       @@SESSION.transaction_isolation AS isolation_level;

실행 결과(MySQL 8.0.x):

mysql> SELECT VERSION() AS mysql_version,
    ->        @@GLOBAL.server_uuid AS backend_uuid,
    ->        CONNECTION_ID() AS connection_id,
    ->        @@GLOBAL.read_only AS read_only,
    ->        @@GLOBAL.super_read_only AS super_read_only,
    ->        @@SESSION.autocommit AS autocommit,
    ->        @@SESSION.transaction_isolation AS isolation_level;

+---------------+--------------------------------------+---------------+-----------+-----------------+------------+-----------------+
| mysql_version | backend_uuid                         | connection_id | read_only | super_read_only | autocommit | isolation_level |
+---------------+--------------------------------------+---------------+-----------+-----------------+------------+-----------------+
| 8.0.46        | 30744984-d71a-11f1-873e-0242ac11000c |            12 |         0 |               0 |          1 | REPEATABLE-READ |
+---------------+--------------------------------------+---------------+-----------+-----------------+------------+-----------------+
1 row in set (0.00 sec)

server_uuid는 올바르게 구성된 서버의 식별에 유용하다. 하지만 datadir 복제 후 UUID까지 중복된 잘못된 구성에서는 독립 식별자로 믿을 수 없다. CONNECTION_ID()도 서버 로컬 값이며 재시작이나 다른 서버 사이에서 전역 고유하지 않다. 실행 시각과 backend 식별자를 함께 남긴다. 출력의 UUID와 연결 번호는 한 번의 검증 실행값이며 운영 기준값이 아니다.

read_only=0, super_read_only=0은 그 서버의 설정을 보여 줄 뿐, 정당한 유일 writer이거나 복제 데이터가 최신이라는 증거가 아니다. 반대로 일반 업무 계정에 대한 쓰기 거부 여부는 계정 권한과 실제 역할을 함께 확인해야 한다. 상태 쿼리만으로 업무 성공을 대신하지 말고 전용 검증 데이터에 대한 쓰기·커밋·조회까지 수행한다.

또한 backend 다중화를 지원하는 Proxy에서는 한 front-end 연결의 서로 다른 트랜잭션이 다른 backend 연결을 사용할 수 있다. 위 조회 한 번을 해당 애플리케이션의 모든 이후 요청에 대한 경로 증명으로 확대하지 않는다.

7. 연결 복구 뒤 남는 문제: 결과가 불명확한 COMMIT

failover 중 연결이 끊겼다는 오류는 직전 업무가 실패했다는 확정 증거가 아니다. 서버가 COMMIT을 완료한 뒤 성공 응답이 유실되었을 수 있다. 반대로 서버가 커밋하지 못하고 세션을 잃었을 수도 있다. 비동기 복제에서 후보로 전달되지 않은 커밋은 또 다른 데이터 손실 문제다.

안전한 애플리케이션 복구는 다음 순서를 따른다.

  1. 오류가 난 물리 연결을 풀에서 제거한다. 망가진 연결을 다시 반환하지 않는다.
  2. 제한된 재시도 예산과 지수 backoff·jitter를 적용해 재접속 폭주를 막는다.
  3. 새 연결에서 문자셋, 타임존, 격리 수준, autocommit 등 필요한 상태를 재설정한다.
  4. 업무 요청의 고유 키로 완료 여부를 확인하고, 중복 방지 규칙에 따라 재실행한다.
  5. 외부 결제·메시지 발행 등 DB 밖의 부수 효과도 별도의 멱등성 또는 조정 절차로 보호한다.

자동 reconnect는 트랜잭션 복원 기능이 아니다. MySQL C API 문서는 재접속 시 활성 트랜잭션, 임시 테이블, 사용자 변수, prepared statement 등 연결 관련 상태가 유지되지 않는다고 설명하며 자동 재접속 기능의 deprecated 상태도 명시한다. 다른 드라이버의 동작은 해당 버전 문서로 확인해야 한다.

TCP connect timeout은 응답 없는 기존 쿼리를 끊는 read/socket timeout과 다르다. 풀에서 연결을 기다리는 acquisition timeout, 쿼리 실행 제한, 전체 요청 deadline도 구별한다. 너무 긴 값은 복구를 늦추고, 지나치게 짧은 값은 정상적인 지연을 장애로 오인해 재시도 폭주를 만들 수 있다.

8. Aurora MySQL에서는 endpoint와 Proxy를 구분한다

Aurora cluster endpoint, 즉 writer endpoint는 현재 writer로 연결하기 위한 이름이다. 승격 시 이름은 유지되지만 가리키는 물리 IP가 바뀔 수 있다. AWS 공식 문서는 연결 풀이나 다중화를 사용할 때 캐시한 DNS 정보를 비우거나 수명을 줄일 준비를 하도록 안내한다. instance endpoint에 writer 주소를 고정하면 cluster endpoint의 자동 대상 변경 이점을 얻지 못한다.

reader endpoint는 쓰기용 대체 경로가 아니다. 장애 전환 직후에는 reader endpoint가 일시적으로 새 primary를 가리킬 수도 있으므로 endpoint 이름만으로 실제 역할을 단정하지 않는다. Aurora의 역할 관리와 저장 계층은 Community 비동기 복제에 VIP를 올린 구성과 다르며, 사용자가 DB 서버에 VRRP를 직접 구성하는 운영 모델도 아니다.

RDS Proxy는 DB writer 변경을 추적하고 backend를 바꾸어 DB endpoint의 DNS 갱신·로컬 캐시로 인한 일부 복구 지연을 줄인다. AWS는 장애 전환 시 유휴 애플리케이션 연결을 유지할 수 있고, 진행 중인 트랜잭션 또는 SQL 문이 있는 연결은 취소될 수 있음을 설명한다. 이는 모든 요청이 무오류로 이어진다거나 진행 중인 트랜잭션이 새 writer에서 계속 실행된다는 보장이 아니다.

따라서 Aurora 리허설도 직접 cluster endpoint 접속과 RDS Proxy 경유를 나누어 수행한다. reader 수와 배치, promotion tier, 드라이버·런타임의 캐시, pinning, 연결 풀의 오류 처리에 따라 체감 중단 시간이 달라진다. 공개 문서의 성능 개선 수치를 자기 서비스의 RTO 보장값으로 복사하지 않는다.

9. 방식 선택과 리허설 체크리스트

VIP를 우선 검토할 조건: 네트워크 주소 이동이 지원되고 관리 권한이 명확하며, 네트워크 경로와 fencing을 함께 검증할 수 있다. DNS 갱신을 줄이는 이점보다 주소 이동 제약이 큰 환경에서는 다른 방식을 검토한다.

DNS를 우선 검토할 조건: 안정된 서비스 이름이 필요하고 다수 환경을 연결해야 하며, 실제 런타임의 이름 재해석과 풀 재연결을 제어할 수 있다. 캐시 정책을 변경할 수 없는 장기 실행 프로세스가 많다면 장애 시험부터 수행한다.

Proxy를 우선 검토할 조건: writer 역할 판단과 접속 정책을 중앙화하거나 backend 연결 재사용이 필요하다. 대신 Proxy 자체의 이중화, 용량, 인증·TLS, 세션 상태 제약을 관리할 수 있어야 한다. InnoDB Cluster에는 메타데이터 연동 Router, Aurora에는 관리형 endpoint와 RDS Proxy처럼 각 시스템의 역할 관리와 맞는 선택지를 비교한다.

10. 정리

VIP는 패킷 경로를, DNS는 이름 해석 결과를, Proxy는 연결의 backend 선택을 바꾼다. 세 방식 모두 writer의 데이터 안전성과 업무 재시도까지 자동으로 완성하지는 않는다. 복구 시간을 줄이려면 가장 늦게 준비되는 계층을 찾고, 경로 변경 성공이 아니라 새 writer에서 업무가 중복 없이 다시 완료되는 시점을 기준으로 삼아야 한다.

이 구분은 이후 failover 자동화와 split-brain 방지 절차의 기초가 된다. 자동화할 대상은 주소 변경 명령 하나가 아니라 역할 권한, 경로 갱신, 연결 복구, 업무 검증으로 이어지는 상태 전이다.

참고 문서