---
title: "Aurora MySQL reader와 replica lag: 커밋 가시성과 reader endpoint"
description: "Aurora MySQL의 ReplicaLag, 커밋 가시성, reader endpoint의 연결 분산을 구분하고 일관성 요구에 맞는 읽기 경로와 진단 절차를 정리한다."
tags: [ MySQL, Aurora, 복제, 트랜잭션, 운영 ]
image: "mysql-report-bg.png"
published: "2026-09-20"
updated: "2026-09-20"
author: "MySQL 기술 노트"
source_url: ""
---

주문 저장은 성공했지만 곧바로 연 주문 상세 화면에는 주문이 없다고 표시되는 경우가 있다. 이때 writer의 커밋 실패, Aurora Replica의 반영 지연, 오래된 트랜잭션 스냅샷, 애플리케이션 캐시를 구분하지 않으면 정상 데이터를 다시 쓰거나 불필요하게 장애 조치를 수행하게 된다.

Aurora의 공유 스토리지는 데이터 내구성을 제공하는 기반이지, 모든 reader의 모든 SELECT가 writer의 최신 커밋을 즉시 보도록 만드는 약속은 아니다. 또한 reader endpoint는 연결을 분산할 뿐, 요청이 필요로 하는 커밋 버전을 판별하지 않는다. 이 글에서는 **저장된 변경, reader가 반영한 변경, 현재 트랜잭션이 볼 수 있는 변경**을 분리하여 읽기 경로를 설계한다.

대상은 같은 리전의 일반적인 Aurora MySQL DB 클러스터다. Global Database의 리전 간 복제, 외부 MySQL과의 binlog 복제는 관측 지표와 제어 방법이 다르므로 별도 범위로 구분한다. 기본 읽기 경로는 write forwarding을 사용하지 않는 구성을 전제로 한다.

## 1. 공유 스토리지에서도 reader 지연이 존재하는 이유

### 커밋의 내구성과 조회의 가시성은 다른 경계다

일반적인 Community MySQL 비동기 복제에서는 source의 binary log를 replica가 수신하고 relay log에 기록한 뒤 적용한다. 반면 같은 Aurora 클러스터의 writer와 reader는 분산 클러스터 볼륨을 공유한다. reader마다 독립적인 전체 데이터 파일 사본을 만들어 binlog의 SQL이나 row event를 적용하는 구조와 동일시하면 안 된다.

공유하는 것은 영속 데이터의 기반이다. 각 DB 인스턴스의 메모리, buffer pool, 실행 중인 트랜잭션 상태까지 하나인 것은 아니다. reader가 변경 정보를 따라잡고 자신이 조회에 사용할 수 있는 상태를 갱신하는 데 시간이 필요하다. writer가 커밋을 완료했다는 사실과, 특정 reader가 그 변경을 읽기 대상으로 제공한다는 사실 사이에는 간격이 생길 수 있다.[1]

AWS 문서는 보통의 Aurora Replica 지연이 100밀리초보다 훨씬 작다고 설명하지만, 이는 상한 보장이나 read-after-write 계약이 아니다. 쓰기 변화량이 많으면 지연이 증가할 수 있다. 따라서 운영 코드에 고정 대기 시간을 넣어 데이터 정합성을 보장하려 해서는 안 된다.

```mermaid
flowchart TD
    A["애플리케이션의 변경 요청"] --> W["writer 트랜잭션 커밋"]
    W --> D["공유 클러스터 볼륨에 내구성 확보"]
    W --> C["클라이언트에 커밋 성공 응답"]
    D --> R["reader가 변경을 반영하여 조회 가능한 상태로 진행"]
    C --> Q["후속 읽기 요청"]
    Q --> E["reader endpoint로 연결 선택"]
    E --> V["선택된 reader의 반영 상태와 Read View로 가시성 결정"]
    R --> V
```

그림은 내구성과 가시성의 경계를 설명한 개념도다. 네트워크 메시지 순서나 Aurora 내부 로그 처리 절차를 그대로 나타낸 것은 아니다. 핵심은 **커밋 성공 응답을 받은 후의 읽기가 자동으로 모든 reader의 반영 완료를 기다리지는 않는다**는 점이다.

### 서로 다른 세 종류의 오래된 읽기

| 구분 | 오래된 값이 보이는 이유 | 우선 확인할 대상 |
|---|---|---|
| 인스턴스 반영 지연 | 선택된 reader가 writer의 변경을 아직 조회에 반영하지 못함 | 해당 reader의 AuroraReplicaLag, 부하, 이벤트 |
| 트랜잭션 스냅샷 | reader가 따라잡아도 기존 Read View가 과거 상태를 유지함 | 격리 수준, 트랜잭션 시작과 첫 읽기 시점 |
| 애플리케이션 경로 | 다른 reader, 캐시, ORM 세션에 남은 값을 읽음 | 실제 연결 대상, 풀 재사용, 캐시 키와 무효화 |

세 문제가 동시에 발생할 수도 있다. 특히 지연 지표가 정상으로 돌아왔는데도 같은 세션에서 이전 값이 계속 보인다면 스냅샷과 캐시를 먼저 분리해야 한다.

## 2. reader endpoint는 쿼리가 아니라 연결을 분산한다

reader endpoint는 새 연결 요청을 사용 가능한 Aurora Replica들에 분산한다. 하나의 연결에서 실행되는 SELECT마다 다른 reader를 선택하지 않으며, 가장 지연이 작은 reader를 고르는 신선도 라우터도 아니다.[2]

이 차이는 connection pool 환경에서 크게 드러난다. 애플리케이션이 시작하면서 생성한 연결이 특정 reader들에 배정되면, 이후 많은 요청이 그 연결들을 재사용한다. reader를 추가했다고 기존 연결이 새 인스턴스로 이동하지는 않는다. 연결 수가 비슷해도 각 연결에서 실행하는 쿼리의 비용이 다르면 CPU와 DB 부하는 불균등할 수 있다.

따라서 다음 세 가지를 따로 관측한다.

- **연결 분포:** 각 DB 인스턴스에 실제 연결이 얼마나 배정되었는가.
- **작업 분포:** 느린 보고서와 짧은 조회가 어느 인스턴스에 집중되는가.
- **신선도 분포:** 애플리케이션이 사용하는 각 reader의 지연과 가시성이 어떠한가.

서로 다른 reader를 오가는 요청은 버전 12를 본 뒤 버전 11을 보는 식의 후퇴도 경험할 수 있다. 같은 reader에 연결을 고정하면 이런 인스턴스 간 후퇴 위험을 줄일 수 있지만, writer에서 방금 커밋한 값을 즉시 볼 수 있다는 보장은 생기지 않는다.

### endpoint 이름만으로 읽기 전용을 강제하지 않는다

클러스터에 reader가 하나도 없으면 reader endpoint는 writer에 연결하며, 그 연결에서 쓰기가 가능할 수 있다.[2] 따라서 읽기 전용 서비스 계정에는 필요한 SELECT 권한만 부여한다. endpoint 문자열의 `cluster-ro`는 권한 경계가 아니다.

장애 조치나 reader 교체 시에는 DNS 캐시와 연결 풀이 서로 다른 수명으로 움직인다. DNS가 새 주소를 알려 주더라도 이미 열린 TCP 연결이 자동으로 다른 인스턴스로 이동하는 것은 아니다. 연결 오류 처리, 새 연결의 대상 확인, 진행 중이던 트랜잭션의 종료를 별도로 설계한다. 풀 전체를 동시에 재생성하면 접속 폭증이 발생할 수 있으므로 점진적인 연결 교체와 재시도 간격도 필요하다.

## 3. ReplicaLag를 읽는 방법: 시간 지표와 커밋 증명을 구분한다

AWS CloudWatch의 정확한 메트릭 이름은 `AuroraReplicaLag`다. 관행적으로 부르는 ReplicaLag라는 말만 보고 일반 RDS의 `ReplicaLag`나 MySQL의 `Seconds_Behind_Source`와 같은 지표로 취급하지 않는다.[3]

| 지표 | 의미 | 운영 해석 |
|---|---|---|
| `AuroraReplicaLag` | 같은 리전 writer에서 해당 reader로 변경이 복제되는 데 걸린 지연, 밀리초 단위 | 실제 조회를 처리한 인스턴스 단위로 확인 |
| `AuroraReplicaLagMaximum` | 클러스터 내 인스턴스들에 대한 최대 복제 지연 | 뒤처진 reader가 있는지 탐지하는 출발점 |
| `AuroraReplicaLagMinimum` | 클러스터 내 인스턴스들에 대한 최소 복제 지연 | 하나라도 빠른 reader가 있다는 사실이지 전체의 정상 증거는 아님 |

여기서 지표 이름의 `Maximum`과 CloudWatch 조회 통계의 `Maximum`은 서로 다르다. 전자는 여러 인스턴스 사이의 최대값이라는 메트릭의 의미이고, 후자는 선택한 시간 구간에 관측된 값들의 최대값을 구하는 집계다. reader별 `AuroraReplicaLag`를 볼 때도 평균만 보면 짧은 급등이 감춰질 수 있다.

또한 AWS는 reader 삭제나 이름 변경 과정에서 `AuroraReplicaLagMaximum`에 일시적 급등이 발생할 수 있다고 명시한다. 이때는 현재 서비스 중인 reader들의 개별 `AuroraReplicaLag`와 변경 이벤트를 대조해야 한다.[3]

### 지연 0은 특정 주문의 가시성 인증서가 아니다

모니터링 값에는 수집 시점, 집계 구간, 조회 시점이 있다. 지연이 0에 가깝다는 표본을 확인한 후 새로 커밋된 변경은 그 표본에 포함되지 않는다. reader A의 정상 지표를 확인하고 실제 쿼리는 reader B에서 실행할 수도 있다. 마지막으로 reader가 변경을 반영했어도 열린 `REPEATABLE READ` 트랜잭션의 스냅샷이 그 변경보다 이전일 수 있다.

따라서 lag 경보는 서비스 상태를 판단하는 도구로 사용하고, 특정 요청의 읽기 일관성을 보장하는 동기화 수단으로 사용하지 않는다. 지표 누락도 0으로 간주하지 말고 관측 불가로 구분한다.

Global Database의 보조 클러스터에서는 `AuroraReplicaLag`가 같은 리전의 replication server와 reader 사이의 지연을 나타내므로, 그 값만으로 원격 리전 writer부터의 전체 지연을 판단해서도 안 된다.[3]

## 4. 커밋 가시성: writer로 읽어도 오래된 스냅샷은 남는다

`REPEATABLE READ`의 일반적인 consistent read는 트랜잭션의 Read View를 사용한다. 같은 트랜잭션의 일반 SELECT를 반복하면 다른 세션이 이후에 커밋한 변경이 보이지 않을 수 있다. 이 상태는 복제 지연과 다르다. `READ COMMITTED`는 문장마다 새 Read View를 사용하지만, 선택된 reader가 아직 반영하지 않은 커밋까지 보이게 만들지는 않는다.

writer에서 커밋한 뒤 writer로 후속 조회를 보내는 전략도 트랜잭션 경계가 전제다. 같은 연결에서 새 트랜잭션이나 autocommit 조회를 시작하거나, 다른 writer 연결이 과거의 읽기 트랜잭션을 계속 사용하지 않도록 해야 한다. 또한 업무 읽기에서는 해당 쓰기가 실제로 성공했는지 먼저 확인해야 한다.

### 검증 예제 1: 서버와 세션의 전제를 확인한다

다음 SQL은 Community MySQL 8.0에서 실행한 공통 진단 예제다. 실제 접속 인스턴스 식별자는 애플리케이션 연결 로그와 RDS 인스턴스 정보를 함께 기록하여 대조한다. `read_only` 하나만으로 관리형 서비스의 모든 권한과 라우팅 상태를 증명할 수는 없다.

```sql
SELECT VERSION() AS mysql_version,
       @@global.read_only AS read_only_mode,
       @@session.transaction_isolation AS isolation_level,
       @@session.autocommit AS autocommit_mode;
```

실행 결과(MySQL 8.0.x):

```text
mysql> SELECT VERSION() AS mysql_version,
    ->        @@global.read_only AS read_only_mode,
    ->        @@session.transaction_isolation AS isolation_level,
    ->        @@session.autocommit AS autocommit_mode;

+---------------+----------------+-----------------+-----------------+
| mysql_version | read_only_mode | isolation_level | autocommit_mode |
+---------------+----------------+-----------------+-----------------+
| 8.0.46        |              0 | REPEATABLE-READ |               1 |
+---------------+----------------+-----------------+-----------------+
1 row in set (0.00 sec)
```

실행 환경은 일반 MySQL의 폐기용 인스턴스이므로 `read_only_mode = 0`인 결과가 나온다. 이것을 Aurora reader의 실제 실행 결과로 해석하지 않는다. 연결 풀을 사용할 때는 풀 초기화 설정과 실제 세션 값이 같은지도 확인한다.

### 검증 예제 2: 복제 없이도 발생하는 오래된 읽기

다음 예제는 **폐기용 Community MySQL 인스턴스에서만 실행**한다. Event Scheduler를 두 번째 세션으로 사용하므로 전역 설정 변경 권한이 필요하며, 운영 서버에서 실습 목적으로 활성화하지 않는다. 원래 Event Scheduler 값이 `DISABLED`이면 이 예제를 위한 별도 테스트 인스턴스를 준비한다.

첫 세션이 named lock을 잡고 Read View를 만든 뒤 잠금을 해제하면, 예약 이벤트가 값을 1에서 2로 바꾸고 커밋한다. named lock은 이벤트가 스냅샷 생성 전에 변경하지 못하도록 순서를 통제하는 용도다. 행 잠금으로 reader 지연을 재현하는 장치는 아니다. `CREATE EVENT`는 암묵적 커밋을 일으키므로 트랜잭션 시작 전에 수행한다.

```sql
SET @saved_event_scheduler = @@global.event_scheduler;
SET GLOBAL event_scheduler = ON;
CREATE TABLE visibility_demo (
    id INT PRIMARY KEY,
    revision INT NOT NULL
) ENGINE=InnoDB;
INSERT INTO visibility_demo VALUES (1, 1);
SELECT GET_LOCK('visibility_demo_gate', 5) AS gate_acquired;
DELIMITER //
CREATE EVENT visibility_demo_update
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 SECOND
DO
BEGIN
    IF GET_LOCK('visibility_demo_gate', 15) = 1 THEN
        UPDATE visibility_demo SET revision = 2 WHERE id = 1;
        DO RELEASE_LOCK('visibility_demo_gate');
    END IF;
END//
DELIMITER ;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
SELECT revision AS initial_revision FROM visibility_demo WHERE id = 1;
DO RELEASE_LOCK('visibility_demo_gate');
DO SLEEP(3);
SELECT revision AS snapshot_revision FROM visibility_demo WHERE id = 1;
SELECT revision AS current_revision FROM visibility_demo WHERE id = 1 FOR UPDATE;
COMMIT;
SELECT revision AS fresh_revision FROM visibility_demo WHERE id = 1;
DROP EVENT IF EXISTS visibility_demo_update;
DROP TABLE visibility_demo;
SET GLOBAL event_scheduler = @saved_event_scheduler;
```

실행 결과(MySQL 8.0.x):

다음은 실제 출력에서 준비·대기·정리 문장을 줄이고, 테이블 생성·입력 성공과 가시성 비교를 발췌한 것이다.

```text
mysql> CREATE TABLE visibility_demo (...);
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO visibility_demo VALUES (1, 1);
Query OK, 1 row affected (0.01 sec)

mysql> SELECT revision AS initial_revision FROM visibility_demo WHERE id = 1;
+------------------+
| initial_revision |
+------------------+
|                1 |
+------------------+
1 row in set (0.00 sec)

mysql> SELECT revision AS snapshot_revision FROM visibility_demo WHERE id = 1;
+-------------------+
| snapshot_revision |
+-------------------+
|                 1 |
+-------------------+
1 row in set (0.00 sec)

mysql> SELECT revision AS current_revision FROM visibility_demo WHERE id = 1 FOR UPDATE;
+------------------+
| current_revision |
+------------------+
|                2 |
+------------------+
1 row in set (0.00 sec)

mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT revision AS fresh_revision FROM visibility_demo WHERE id = 1;
+----------------+
| fresh_revision |
+----------------+
|              2 |
+----------------+
1 row in set (0.00 sec)
```

검증에서 확인할 핵심은 `initial_revision = 1`, `snapshot_revision = 1`, `current_revision = 2`, `fresh_revision = 2`다. 대기 3초는 실습 이벤트를 기다리기 위한 편의값이며 일관성 보장책이 아니다. 재현 환경이 느리면 이벤트 실행 여부와 네 값을 다시 확인해야 한다.

`FOR UPDATE` 결과는 두 번째 세션의 커밋이 이미 끝났다는 사실을 같은 서버에서 비교하기 위해 넣었다. 이것을 Aurora reader에서 최신값을 얻는 해결책으로 사용하지 않는다. 읽기 전용 인스턴스의 허용 범위와 별개로, 잠금 읽기가 reader에 아직 반영되지 않은 변경을 writer에서 끌어오는 것은 아니다.

이 실험은 **InnoDB의 스냅샷과 current read 차이만 검증**한다. Aurora의 내부 복제 지연, reader endpoint 분산, CloudWatch 수치는 이 로컬 실험으로 재현하지 않았으며 AWS 공식 문서에 근거하여 설명한다.

## 5. 업무가 요구하는 일관성에 따라 읽기 경로를 정한다

### 저장 직후 상세 조회는 writer에서 시작한다

결제 결과, 주문 생성 직후 상세 조회, 권한 변경 직후 접근 판정처럼 방금 수행한 쓰기를 반드시 봐야 하는 작업은 커밋 성공 이후 writer에서 새 읽기 경계를 열어 조회하는 방식이 단순하다. 읽기를 전부 reader로 보내고 결과가 없으면 재삽입하는 방식은 중복 처리와 상태 역전의 원인이 된다.

후속 읽기만 writer로 보내는 정책과 전체 요청을 writer에 고정하는 정책 중에서 업무 단위에 맞는 방식을 선택한다. 고정 기간을 몇 초로 정하더라도 이는 지연 분포에 근거한 성능 정책이지 영구적인 일관성 보장은 아니다.

### 지연 허용 조회에는 버전과 제한 시간을 둔다

상품 목록, 통계 화면처럼 약간 오래된 값을 허용하는 읽기는 reader에 적합하다. 필요한 경우 업무 행의 증가하는 `revision`을 사용하여 요청이 요구한 버전 이상인지 확인할 수 있다. 단순 서버 시간이나 auto-increment 값만으로 여러 트랜잭션의 커밋 순서를 추론하지 않는다.

버전 확인은 같은 업무 객체와 트랜잭션 설계를 전제로 한다. marker 하나가 보인다는 사실을 클러스터의 모든 테이블과 모든 후속 커밋이 최신이라는 증거로 확대하지 않는다. 삭제는 행의 부재만으로 버전을 판별하기 어려우므로 tombstone이나 별도 변경 이력 등 다른 모델이 필요하다.

제한 시간 안에 필요한 버전이 보이지 않으면 writer로 전환하거나 명시적인 재시도 응답을 반환한다. 확인을 반복할 때는 오래된 `REPEATABLE READ` 트랜잭션을 계속 재사용하지 않는다. 한 번 신선한 값을 읽은 reader와 실제 업무 조회를 실행하는 reader가 달라지지 않도록 연결 경계도 유지한다.

| 요구 사항 | 우선 선택 | 반드시 함께 설계할 것 |
|---|---|---|
| 자신의 쓰기를 즉시 조회 | 커밋 후 writer의 새 읽기 | 트랜잭션 경계, 쓰기 성공 확인 |
| 약간의 오래된 값 허용 | reader endpoint | 지연 예산, 캐시 정책, 인스턴스별 관측 |
| 특정 revision 이상 필요 | 같은 연결에서 버전 확인과 읽기 | 제한 시간, 스냅샷 갱신, writer 전환 |
| 장시간 통계·보고서 | 별도 reader 또는 custom endpoint | 온라인 조회와 부하 격리, 구성원 관리 |

### write forwarding의 일관성 설정은 별도 기능이다

지원 버전과 설정을 갖추고 write forwarding을 활성화했다면 `aurora_replica_read_consistency`의 `EVENTUAL`, `SESSION`, `GLOBAL`을 검토할 수 있다. AWS 문서에 따르면 `SESSION`은 해당 세션에서 전달한 쓰기가 보이도록 필요시 기다리고, `GLOBAL`은 쿼리 시작 시점까지 writer에서 커밋된 변경이 보이도록 기다린다.[5]

이 설정은 **write forwarding이 활성화된 클러스터 또는 인스턴스에만 영향을 준다**. 별도 writer 연결에서 수행한 쓰기를 reader 연결의 `SESSION` 설정이 자동으로 추적한다고 생각해서도 안 된다. 일관성을 높이면 읽기 지연에 반영 대기 시간이 더해질 수 있다. 일반 reader endpoint에 변수를 하나 설정하는 것만으로 모든 읽기가 강한 일관성을 얻는 기능은 아니다. 지원 엔진 버전, 세션 적용 범위, 허용 SQL과 트랜잭션 제약을 확인한 뒤 별도 시험한다.

## 6. 장애 진단: 실제 연결 대상부터 확인한다

### 1단계: 응답 경로와 시간 경계를 기록한다

문제가 발생한 업무 키 또는 revision, writer의 커밋 성공 응답 시각, 후속 조회의 시작·종료 시각, 실제 DB 인스턴스, 세션 격리 수준, 풀에서 꺼낸 연결의 트랜잭션 상태를 확보한다. 고객 데이터 원문 대신 요청 식별자와 필요한 최소 메타데이터를 사용한다.

동일한 쓰기 이후 reader의 새 트랜잭션과 writer의 새 트랜잭션에서 값을 비교한다. reader만 오래된 값을 보여 주는지, 동일 세션의 스냅샷만 오래된지, DB 직접 조회는 정상인데 애플리케이션만 오래된지 나누면 조사 범위가 줄어든다.

### 2단계: Aurora 전용 상태를 writer에서 확인한다

Aurora는 `information_schema.replica_host_status`를 제공한다. 공개된 열에는 `SERVER_ID`, `SESSION_ID`, `IS_CURRENT`, `LAST_UPDATE_TIMESTAMP`, `REPLICA_LAG_IN_MILLISECONDS` 등이 있다. AWS는 reader가 뒤처졌을 때 그 reader에서 조회한 정보도 오래되었을 수 있으므로 writer에서 조회할 것을 권장한다.[4]

아래는 **Aurora 전용 진단 SQL의 구조 예시**다. Community MySQL에는 해당 테이블이 없으며, 이 글에서는 Aurora 실인스턴스에서 실행하지 않았다. 대상 엔진 버전의 열과 권한을 확인한 후 사용하며, 실행 결과를 가정하여 싣지 않는다.

```text
SELECT SERVER_ID, SESSION_ID, IS_CURRENT,
       LAST_UPDATE_TIMESTAMP, REPLICA_LAG_IN_MILLISECONDS
FROM information_schema.replica_host_status
ORDER BY SERVER_ID;
```

지연값만 보지 말고 `LAST_UPDATE_TIMESTAMP`가 갱신되는지, 반환된 인스턴스가 현재 서비스 구성원인지도 확인한다. `IS_CURRENT`를 특정 업무 트랜잭션의 가시성 인증으로 사용하지 않는다. 일반 MySQL의 `SHOW REPLICA STATUS`는 외부 binlog 복제 등을 확인할 때 필요한 명령이지, 같은 Aurora 클러스터의 모든 reader 상태를 대신하는 명령은 아니다.

### 3단계: 지연과 부하의 상관관계를 구분한다

모든 reader가 동시에 뒤처지면 writer의 쓰기량 급증과 클러스터 이벤트를 먼저 대조한다. 특정 reader만 뒤처지면 해당 인스턴스의 CPU, FreeableMemory, DB 부하와 쿼리 지연, 연결 집중, 재시작·크기 변경 이력을 확인한다. 긴 분석 쿼리와 작은 reader 사양이 겹치면 자원 경합을 의심할 수 있지만, CPU 상승 하나만으로 원인을 확정하지 않는다.

부하가 낮은데 애플리케이션의 값만 오래되면 DNS 캐시, 연결 풀, ORM의 읽기 트랜잭션 유지, 애플리케이션 캐시를 조사한다. 지연 지표가 내려갈 때까지 무한 재시도하는 방식은 부하를 더할 수 있으므로 피한다.

### 4단계: 우회와 복귀 조건을 함께 정한다

지연이 큰 reader를 읽기 대상에서 제외하거나 중요 조회를 writer로 전환할 수 있다. 다만 writer의 잔여 용량을 확인하지 않고 모든 읽기를 한꺼번에 옮기면 쓰기 응답까지 악화된다. 우회는 요청 종류별로 제한하고, 복귀는 여러 관측 구간의 지연 회복과 실제 canary 조회 성공을 함께 조건으로 둔다.

reader endpoint 자체에 사용자 정의 lag 임계값을 넣어 자동 제외하는 기능이 있다고 가정하지 않는다. custom endpoint나 애플리케이션 라우팅을 사용한다면 구성원 변경, 기존 연결 정리, 장애 시 동작도 운영 책임에 포함된다.

## 7. 운영 체크리스트

- [ ] 같은 클러스터의 Aurora Replica, 외부 binlog replica, Global Database의 범위를 구분했는가.
- [ ] 중요한 읽기에 read-after-write 또는 허용 지연 계약을 명시했는가.
- [ ] 지표 이름을 `AuroraReplicaLag`로 확인하고 밀리초 단위로 해석했는가.
- [ ] 클러스터 최소·평균값뿐 아니라 실제 사용 reader별 값과 시간 구간 최대값을 확인했는가.
- [ ] 지표 누락, reader 삭제·이름 변경, 재시작을 지연 급등과 대조했는가.
- [ ] reader endpoint의 연결 분산을 쿼리 분산 또는 신선도 보장으로 오해하지 않는가.
- [ ] 연결 풀의 트랜잭션 종료와 세션 초기화, DNS·연결 교체 정책을 시험했는가.
- [ ] 읽기 계정의 권한으로 쓰기를 제한하고 endpoint 이름에 의존하지 않는가.
- [ ] writer 전환 시에도 과거 Read View와 애플리케이션 캐시가 남지 않는가.
- [ ] 버전 확인 재시도에 제한 시간, 새 읽기 경계, 실패 시 경로를 정했는가.
- [ ] write forwarding을 쓴다면 지원 버전과 일관성 모드의 세션 범위를 별도로 검증했는가.
- [ ] 부하 우회 시 writer 용량과 정상화 후 복귀 조건까지 정의했는가.

## 8. 정리

Aurora reader 운영의 기준은 지연 수치를 작게 유지하는 것만이 아니다. **커밋 내구성, 인스턴스 반영, 트랜잭션 가시성, 연결 라우팅을 서로 다른 경계로 다루는 것**이 핵심이다. reader endpoint는 읽기 용량을 확장하고, ReplicaLag는 지연을 관측하며, 업무의 읽기 일관성은 올바른 경로와 트랜잭션 설계로 확보한다.

이 구분은 이후 Aurora 장애 조치와 endpoint 전환을 이해하는 기반이 된다. 연결이 새 writer에 도달했다는 사실, 이전 쓰기의 결과를 확인했다는 사실, 재시도해도 중복 처리가 없다는 사실 역시 각각 검증해야 한다.

## 참고 문서

1. [AWS — Aurora 복제 구조](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Replication.html)
2. [AWS — reader endpoint의 연결 분산과 reader가 없는 경우의 동작](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Endpoints.Reader.html)
3. [AWS — Aurora CloudWatch 메트릭 정의](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.AuroraMonitoring.Metrics.html)
4. [AWS — Aurora MySQL 전용 information_schema 테이블](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Reference.ISTables.html)
5. [AWS — 로컬 write forwarding의 읽기 일관성](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-mysql-write-forwarding-consistency.html)
