카테고리 : MySQL/기술노트

Replication 에러 처리: duplicate key, missing row, skip counter의 위험

MySQL 복제의 1062·1032 오류를 원인과 트랜잭션 경계로 진단하고, 데이터 복구와 skip counter·GTID 스킵의 위험을 재현으로 확인한다.

저자: MySQL 기술 노트 작성: 2026.09.17 약 12분 7,094자
다운로드

복제 적용이 멈추면 먼저 지연을 줄이고 싶어진다. 그러나 1062 Duplicate entry1032 Can't find record는 단순한 재시도 대상이 아니라 source가 전제한 데이터와 replica가 가진 데이터가 다를 수 있다는 신호다. 오류를 지워 SQL thread를 다시 실행하는 것과 데이터 정합성을 복원하는 것은 서로 다른 작업이다.

이 장에서는 오류가 발생하는 경로, 안전한 원인 조사, 데이터 수정 후 재적용과 스킵의 차이를 설명한다. 특히 한 트랜잭션에서 오류가 난 행만 보다가 같은 트랜잭션의 다른 테이블 변경까지 버리는 위험을 실제 복제로 확인한다. 기본 설명은 MySQL 8.0 이상 InnoDB·ROW 복제를 기준으로 하며, 8.4 LTS에서도 적용되는 원칙과 관리형 Aurora의 차이를 구분한다.

1. 수신 성공과 적용 성공은 별개의 상태다

Source의 커밋은 binary log에 트랜잭션을 기록한다. Replica의 receiver는 이를 relay log로 받아들이고, coordinator와 worker가 로컬 테이블에 적용한다. Receiver의 연결이 정상이어도 worker는 유일성 제약 위반이나 적용 대상 행 부재로 멈출 수 있다. 이때 새 이벤트는 계속 수신되어 relay log가 커질 수 있다.

flowchart TD
    A[Source에서 트랜잭션 커밋] --> B[Receiver가 relay log에 수신]
    B --> C[Coordinator가 worker에 배정]
    C --> D{Replica에 행 이벤트 적용}
    D -->|제약과 행 상태 일치| E[커밋 및 실행 이력 반영]
    D -->|1062 또는 1032| F[적용 중단 및 오류 증거 보존]
    F --> G{트랜잭션 전체를 복원할 수 있는가}
    G -->|국소 불일치와 원인 확인| H[데이터 보정 후 원래 트랜잭션 재적용]
    G -->|범위 불명 또는 광범위 불일치| I[일관된 백업으로 재구축]
    F --> J[예외 승인 아래 스킵]
    J --> K[실행 이력은 전진하지만 데이터 누락 가능]

InnoDB에서 실패한 적용 트랜잭션은 롤백되는 것이 기본이다. 그렇다고 replica 전체가 오류 트랜잭션 바로 전의 전역 시점에 정지했다고 가정해서는 안 된다. 병렬 worker는 다른 트랜잭션을 이미 처리했을 수 있으며, 커밋 순서 보존 설정도 영향을 준다. 비트랜잭션 테이블이 섞였다면 변경의 원자적 롤백 자체도 보장되지 않는다.

Seconds_Behind_Source, receiver의 ON, 실행 GTID의 전진은 각각 다른 관측값이다. 단일 지표로 복구 완료를 판정하지 않는다.

2. 1062와 1032가 알려 주는 불일치

오류 ROW 적용에서의 의미 우선 확인할 사항
1062, duplicate key INSERT나 UPDATE 결과가 replica의 PRIMARY/UNIQUE 제약과 충돌한다. 충돌한 인덱스, 실제 키 값, replica 직접 쓰기, 중복 적재, source와 다른 UNIQUE 정의
1032, missing row UPDATE/DELETE의 before image에 대응하는 행을 replica에서 찾지 못했다. 누락된 행, 이전 변경 누락, 데이터/스키마 차이, 잘못된 복제 시작 경계

1062에서 충돌 키는 항상 PRIMARY KEY가 아니다. UNIQUE(email) 충돌일 수도 있으므로 오류 메시지의 인덱스를 먼저 확인한다. 이미 행이 있다는 사실만으로 원래 트랜잭션이 적용되었다고 판단해서도 안 된다. 행의 다른 열 값이나 같은 트랜잭션의 감사 기록은 다를 수 있다.

1032도 단순히 “현재 source에는 있는데 replica에는 없다”로만 해석할 수 없다. ROW 이벤트는 before image와 테이블의 인덱스·행 검색 방식에 따라 적용 대상을 찾는다. 키 값의 변경, 컬럼 정의 차이, 일치해야 할 기존 값의 차이까지 조사한다. binlog_row_image=MINIMAL이면 기록된 열이 제한되므로 이벤트만으로 완전한 과거 행을 복원할 수 없을 수도 있다.

Statement 기반 복제에는 statement의 결정성, 실행 환경, 필터의 기본 DB 판정 같은 별도 변수가 있다. 이 장의 ROW 재현을 statement 기반 장애에 그대로 대입하지 않는다.

3. 먼저 보존하고, 그다음 고친다

운영 대응은 다음 순서가 안전하다.

  1. 장애 replica를 읽기 라우팅과 자동 승격 후보에서 격리한다. Source 서비스를 중단해야 한다는 뜻은 아니다.
  2. 채널, source 식별자, 오류 시각, receiver/coordinator/worker 상태, 실행·수신 GTID, 적용 위치와 스키마 정의를 보존한다. SHOW REPLICA STATUS만 보지 말고 worker별 오류를 확인한다.
  3. 해당 worker의 LAST_ERROR_MESSAGEAPPLYING_TRANSACTION을 확보한다. 다른 worker의 마지막 성공 GTID를 실패 GTID로 오인하지 않는다.
  4. 스킵이나 복제 좌표 변경 전에는 해당 채널의 receiver와 applier를 모두 정지하고 증거를 다시 보존한다. 재시작·초기화는 오류 상태를 바꾸므로 먼저 기록해야 한다.
  5. 실제 binary log 또는 relay log에서 실패 GTID의 시작부터 커밋까지 확인한다. 같은 트랜잭션의 다른 테이블 변경도 조사한다.
  6. 원인과 불일치 범위가 확정되면 데이터 보정 후 재적용, 일관된 스냅샷으로 재구축, 승인된 예외 스킵 중 하나를 선택한다.

ROW 이벤트 내용은 mysqlbinlog --base64-output=DECODE-ROWS -vv로 해석할 수 있다. 파일·위치는 실제 관측값을 사용하고, 실패 이벤트 한 개가 아니라 GTID와 트랜잭션 경계를 포함해 읽는다. 디코딩한 주석형 행 표현은 그대로 실행할 복구 SQL이 아니다. RESET REPLICA, GTID 초기화, 임의의 위치 전진은 원인 조사용 명령이 아니며 증거와 복구 경계를 훼손할 수 있다.

4. 재현 1: 충돌 행을 바로잡고 원래 트랜잭션을 적용한다

아래 예제는 운영 테이블이 아니라 비어 있는 실습 스키마를 전제로 한다. 공개 SQL의 문법과 실행은 폐기용 MySQL 8.0에서 검사했고, 복제 오류와 GTID 스킵은 별도 source/replica 두 인스턴스에서 검증했다. 두 인스턴스는 GTID 자동 위치 지정, ROW, InnoDB, 병렬 worker를 사용했다. MySQL 8.4에서 같은 실험을 다시 실행한 결과는 아니다.

먼저 source에 업무 테이블과 감사 테이블을 만든다. 실습에서는 mysql_tech_note 스키마가 이미 준비되어 있다.

USE mysql_tech_note;
CREATE TABLE repl_error_item (
    id BIGINT PRIMARY KEY,
    note VARCHAR(40) NOT NULL
) ENGINE=InnoDB;
CREATE TABLE repl_error_audit (
    id BIGINT PRIMARY KEY,
    item_id BIGINT NOT NULL,
    action_name VARCHAR(40) NOT NULL
) ENGINE=InnoDB;
INSERT INTO repl_error_item VALUES (1, 'initial'), (2, 'before_update');

실행 결과(MySQL 8.0.x):

mysql> CREATE TABLE repl_error_item (
    ->     id BIGINT PRIMARY KEY,
    ->     note VARCHAR(40) NOT NULL
    -> ) ENGINE=InnoDB;

Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE repl_error_audit (
    ->     id BIGINT PRIMARY KEY,
    ->     item_id BIGINT NOT NULL,
    ->     action_name VARCHAR(40) NOT NULL
    -> ) ENGINE=InnoDB;

Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO repl_error_item VALUES (1, 'initial'), (2, 'before_update');

Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

이 초기 데이터가 replica까지 적용된 것을 확인한 뒤, 실험을 위해 replica에만 id=3, note='replica_only' 행을 넣었다. 이는 불일치를 만드는 고의적인 실습 조작이다. 로컬 조작은 별도 세션에서 binary logging을 끈 상태로 실행하여, 로컬 GTID 생성과 데이터 불일치의 효과를 분리했다. 운영에서는 sql_log_bin=0을 일반적인 복구 절차로 사용하면 안 된다. 하위 replica, 감사 가능성, 백업 복구 이력에 영향을 준다.

이제 source에서 다음 트랜잭션을 커밋한다.

USE mysql_tech_note;
START TRANSACTION;
INSERT INTO repl_error_item VALUES (3, 'source_insert');
INSERT INTO repl_error_audit VALUES (101, 3, 'insert_item');
COMMIT;

실행 결과(MySQL 8.0.x):

mysql> START TRANSACTION;

Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO repl_error_item VALUES (3, 'source_insert');

Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO repl_error_audit VALUES (101, 3, 'insert_item');

Query OK, 1 row affected (0.00 sec)

mysql> COMMIT;

Query OK, 0 rows affected (0.00 sec)

Source에서는 정상 커밋되지만 replica에서는 id=3의 PRIMARY KEY가 충돌한다. 다음 진단은 오류 발생 직후 replica에서 실행한 결과다. 채널을 여러 개 운영한다면 대상 CHANNEL_NAME 조건을 추가한다. 여기의 빈 채널명은 기본 채널이다.

SELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER
FROM performance_schema.replication_connection_status;
SELECT WORKER_ID, SERVICE_STATE, LAST_ERROR_NUMBER
FROM performance_schema.replication_applier_status_by_worker
WHERE LAST_ERROR_NUMBER <> 0
ORDER BY WORKER_ID;

실행 결과(MySQL 8.0.x):

mysql> SELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER
    -> FROM performance_schema.replication_connection_status;

+--------------+---------------+-------------------+
| CHANNEL_NAME | SERVICE_STATE | LAST_ERROR_NUMBER |
+--------------+---------------+-------------------+
|              | ON            |                 0 |
+--------------+---------------+-------------------+
1 row in set (0.00 sec)

mysql> SELECT WORKER_ID, SERVICE_STATE, LAST_ERROR_NUMBER
    -> FROM performance_schema.replication_applier_status_by_worker
    -> WHERE LAST_ERROR_NUMBER <> 0
    -> ORDER BY WORKER_ID;

+-----------+---------------+-------------------+
| WORKER_ID | SERVICE_STATE | LAST_ERROR_NUMBER |
+-----------+---------------+-------------------+
|         1 | OFF           |              1062 |
+-----------+---------------+-------------------+
1 row in set (0.00 sec)

Receiver가 ON, receiver 오류가 0이어도 worker에는 1062가 기록된다. 네트워크 재접속으로 해결할 문제라고 단정할 수 없다. 병렬 복제에서는 coordinator의 오류가 worker 실패를 요약하는 형태일 수 있으므로 실제 worker 메시지까지 내려가야 한다.

이 재현에서는 replica의 잘못된 id=3 행만 제거하고 원래 트랜잭션을 다시 적용했다. 재적용 뒤에는 note='source_insert'와 감사 행 id=101함께 존재함을 확인했다. 해결한 것은 오류 번호가 아니라 “이 트랜잭션을 다시 적용해도 되는 이전 상태”다.

운영에서는 충돌 행을 무조건 지우지 않는다. 그 행이 정당한 업무 데이터를 담고 있을 수 있고, 충돌 원인이 다른 UNIQUE 키일 수도 있다. 보정 전에 행 전체, 관련 제약, 해당 트랜잭션의 모든 변경을 비교한다. 현재 source 값을 그대로 덮어써도 된다는 보장은 없다. Source가 이미 더 진행했다면 앞으로 적용될 이벤트의 before image와 다시 충돌할 수 있다.

5. 재현 2: GTID를 스킵하면 다른 테이블의 변경도 사라진다

첫 번째 복구가 끝난 뒤 replica에서만 id=2를 삭제했다. Source에는 원래 행이 남아 있다. Source에서 다음 트랜잭션을 실행한다.

USE mysql_tech_note;
START TRANSACTION;
UPDATE repl_error_item SET note = 'after_update' WHERE id = 2;
INSERT INTO repl_error_audit VALUES (102, 2, 'update_item');
COMMIT;

실행 결과(MySQL 8.0.x):

mysql> START TRANSACTION;

Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE repl_error_item SET note = 'after_update' WHERE id = 2;

Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> INSERT INTO repl_error_audit VALUES (102, 2, 'update_item');

Query OK, 1 row affected (0.00 sec)

mysql> COMMIT;

Query OK, 0 rows affected (0.00 sec)

Replica의 UPDATE 적용은 1032로 실패했다. 같은 진단 쿼리로 receiver는 정상이고 worker에 1032가 기록된 것을 확인했다. 감사 테이블의 INSERT는 별도의 업무 행이지만 같은 트랜잭션에 속하므로 정상 커밋된 것으로 취급할 수 없다.

이어 실험에서만 다음 순서로 실패 GTID를 빈 트랜잭션으로 기록했다. 아래는 절차 설명용 템플릿이며 <실패_GTID>는 실제 실패 worker와 로그에서 확정한 단일 GTID로 치환해야 한다. 운영에 그대로 붙여 넣는 처방이 아니다. 실험에서는 해당 값으로 실제 실행했으며, 실패 트랜잭션 전체가 생략되는 위험을 확인하는 데 사용했다.

STOP REPLICA;
SET GTID_NEXT = '<실패_GTID>';
BEGIN;
COMMIT;
SET GTID_NEXT = 'AUTOMATIC';
START REPLICA;

GTID_NEXT 설정부터 AUTOMATIC 복원까지는 같은 관리 세션에서 실행한다. 다중 source에서는 정지·시작할 채널을 명시하고, GTID 실행 이력 자체는 서버 범위라는 점을 고려해야 한다. 권한과 super_read_only 정책도 사전에 검토한다. 관리 제한을 일괄 해제하는 방식으로 진행하지 않는다.

스킵 후 아래 값을 replica에서 확인했다.

SELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER,
       GTID_SUBSET(RECEIVED_TRANSACTION_SET,
                   @@GLOBAL.gtid_executed) AS received_executed
FROM performance_schema.replication_connection_status;

실행 결과(MySQL 8.0.x):

mysql> SELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER,
    ->        GTID_SUBSET(RECEIVED_TRANSACTION_SET,
    ->                    @@GLOBAL.gtid_executed) AS received_executed
    -> FROM performance_schema.replication_connection_status;

+--------------+---------------+-------------------+-------------------+
| CHANNEL_NAME | SERVICE_STATE | LAST_ERROR_NUMBER | received_executed |
+--------------+---------------+-------------------+-------------------+
|              | ON            |                 0 |                 1 |
+--------------+---------------+-------------------+-------------------+
1 row in set (0.00 sec)

received_executed=1은 현재 수신 GTID 집합이 실행 집합에 포함된다는 뜻이다. 모든 데이터가 일치한다는 뜻도, source의 아직 수신하지 않은 최신 변경까지 따라잡았다는 뜻도 아니다. 이 실험에서는 별도로 캡처한 source의 실행 GTID 집합이 replica 실행 집합에 포함되는 것까지 확인했다. 그래도 아래 데이터 차이는 남았다.

다음 조회는 source와 replica 각각에서 실행한다. 출력은 두 서버를 구분해 제시한다.

USE mysql_tech_note;
SELECT id, note FROM repl_error_item ORDER BY id;
SELECT id, item_id, action_name FROM repl_error_audit ORDER BY id;

실행 결과(MySQL 8.0.x):

Source

mysql> SELECT id, note FROM repl_error_item ORDER BY id;

+----+---------------+
| id | note          |
+----+---------------+
|  1 | initial       |
|  2 | after_update  |
|  3 | source_insert |
+----+---------------+
3 rows in set (0.00 sec)

mysql> SELECT id, item_id, action_name FROM repl_error_audit ORDER BY id;

+-----+---------+-------------+
| id  | item_id | action_name |
+-----+---------+-------------+
| 101 |       3 | insert_item |
| 102 |       2 | update_item |
+-----+---------+-------------+
2 rows in set (0.00 sec)

Replica — 실패 GTID를 빈 트랜잭션으로 기록한 뒤

mysql> SELECT id, note FROM repl_error_item ORDER BY id;

+----+---------------+
| id | note          |
+----+---------------+
|  1 | initial       |
|  3 | source_insert |
+----+---------------+
2 rows in set (0.00 sec)

mysql> SELECT id, item_id, action_name FROM repl_error_audit ORDER BY id;

+-----+---------+-------------+
| id  | item_id | action_name |
+-----+---------+-------------+
| 101 |       3 | insert_item |
+-----+---------+-------------+
1 row in set (0.00 sec)

Replica에는 id=2 업무 행이 없고 감사 행 id=102도 없다. Source에는 변경된 업무 행과 감사 행이 모두 존재한다. 실패 GTID를 빈 트랜잭션으로 커밋하는 것은 해당 GTID의 업무 내용을 복구하는 작업이 아니라, 그 내용을 적용하지 않고 처리 이력만 채우는 작업이다.

이미 처리된 GTID로 기록했으므로 단순 재접속이나 START REPLICA 재실행으로 누락된 트랜잭션을 자동 복구하지 못한다. 사후 데이터 보정은 별도 복구 작업이며, 이력을 임의로 지워 다시 받으려 하면 다른 트랜잭션까지 영향을 줄 수 있다. 빈 트랜잭션이 replica의 binary log에 기록되었다면 하위 복제나 향후 승격 후 토폴로지에도 그 생략의 의미가 전파될 수 있다.

6. skip counter의 1은 오류 행 하나가 아니다

비GTID 복제에서 사용하는 sql_replica_skip_counter는 이벤트를 건너뛰는 기능이다. sql_slave_skip_counter라는 이전 이름으로 작성된 문서도 있지만, 8.0.26 이상과 8.4에서는 새 이름을 우선한다. gtid_mode=ON에서는 이 counter 방식 대신 GTID에 맞는 절차를 검토해야 한다.

Binary log는 트랜잭션·이벤트 그룹·여러 행 이벤트로 구성된다. InnoDB의 이벤트 그룹은 트랜잭션에 대응하며, counter가 그룹 중간에 도달하면 그룹 끝까지 더 건너뛴다. 따라서 counter를 1로 지정해도 “충돌한 행 한 개만 무시하고 나머지는 적용”한다는 뜻이 아니다. 압축된 Transaction_payload_event는 내부 여러 이벤트를 포함해 하나의 counter 값으로 세어진다.

방식 실제 의미 운영상 위험
비GTID skip counter 지정한 이벤트 수를 건너뛰고 필요하면 이벤트 그룹 끝까지 진행 같은 트랜잭션의 정상 변경까지 생략, 과다 지정 시 다음 그룹까지 생략
GTID 빈 트랜잭션 특정 GTID를 데이터 변경 없이 실행 이력에 기록 트랜잭션 전체 누락, GTID 집합 비교만으로 검출 불가
파일·위치 직접 변경 지정한 source log 좌표에서 다시 수신 트랜잭션 경계를 잘못 고르면 추가 누락·오류 발생
오류 번호 자동 무시 지정 오류를 지속적으로 억제 불일치를 장기적으로 누적하고 감시 신호 상실

비GTID counter는 해당 조건에서 receiver와 applier를 모두 정지한 뒤 설정하고, 다음 START REPLICA 때 적용된다. 다중 source에서는 재시작 채널을 잘못 지정하면 다른 채널에 영향을 준다. 이 장의 실제 스킵 실험은 GTID 방식이며, 비GTID counter 동작 설명은 공식 문서를 기준으로 했다. counter 실험까지 수행한 것으로 해석하지 않는다.

replica_exec_mode=IDEMPOTENT나 오류 무시 목록을 일반적인 정합성 복구책으로 사용하는 것도 피한다. 같은 오류의 반복을 덜 보이게 할 뿐, 빠진 감사 기록과 업무 행을 채우지는 않는다. 네트워크 일시 장애나 재시도 가능한 잠금 충돌과 달리 1062·1032는 데이터 전제가 달라진 원인을 해결하지 않으면 재시작만으로 나아지지 않는 경우가 많다.

7. 보정·스킵·재구축을 고르는 기준

보정 후 재적용은 불일치가 국소적이고, 실패 트랜잭션 직전의 올바른 replica 상태를 복원할 근거가 있을 때 선택한다. 보정 범위와 downstream 영향까지 기록하고, 재적용 뒤 트랜잭션 전체의 결과를 비교한다.

예외 스킵은 생략의 업무 의미를 설명할 수 있고, 해당 트랜잭션의 효과가 이미 모두 존재하거나 승인된 수동 보정으로 완성되었음을 확인할 때에만 검토한다. 중복 키 한 행이 존재한다는 사실은 그 근거가 되지 않는다. 감사·집계·다른 테이블 변경까지 포함해야 한다. 데이터 폐기를 의도했다면 허용 범위와 사후 조치를 명시적으로 기록한다.

재구축은 여러 테이블에서 반복 오류가 나거나, 잘못된 백업·복제 시작점·장시간 직접 쓰기로 불일치 범위를 확정할 수 없을 때 유리하다. 연속 스킵으로 복제를 정상처럼 보이게 만드는 것보다 일관된 스냅샷과 올바른 GTID/좌표에서 새 replica를 만드는 편이 검증 가능하다. 필요한 binary log 보존 시간과 재구축 중 용량·전송 부하도 함께 계산한다.

복구 완료는 다음 세 층을 모두 만족해야 한다.

  • 실행 상태: 대상 채널의 receiver, coordinator, worker가 정상이고 새 오류가 발생하지 않는다.
  • 진행 경계: 캡처한 source GTID/좌표까지 적용을 확인하고 이후 지연과 relay log 적체가 감소한다.
  • 데이터 의미: 사고 범위의 PK 집합·열 값·유일성·관련 감사/집계 행을 일관된 경계에서 비교한다.

행 수가 같거나 일부 샘플이 맞는 것만으로 전체 정합성을 단정하지 않는다. 전체 비교가 필요하면 일관된 읽기 경계와 chunk 단위 비교를 설계하고, 계속 쓰는 source의 현재 값과 지연된 replica 값을 무작정 비교해 허위 불일치를 만들지 않는다.

8. Aurora MySQL에서는 복제 경로를 먼저 구분한다

같은 Aurora 클러스터의 reader는 일반적인 외부 MySQL binlog replica와 동일한 구조가 아니다. 같은 클러스터 reader 문제에 STOP REPLICA나 skip counter 절차를 그대로 적용하지 않는다. Aurora Global Database의 스토리지 기반 복제도 별도 경로다.

반면 외부 MySQL에서 Aurora MySQL로 들어오는 binary log 복제라면 행 불일치와 트랜잭션 스킵의 의미는 여전히 중요하다. 이때는 Aurora 엔진 버전에서 제공하는 mysql.rds_* 관리 프로시저와 권한 제한을 사용해야 할 수 있다. Community MySQL의 root 명령을 그대로 실행할 수 있다고 가정하지 않는다. 일부 관리형 스킵 프로시저의 지원 여부와 GTID 제한은 엔진별 공식 문서를 확인해야 한다.

관리형 프로시저도 데이터를 자동으로 맞춰 주지는 않는다. 읽기 엔드포인트에서의 제외, 승격 후보 제한, source 이력 보존, 사후 정합성 검증은 여전히 필요하다. 이 글의 실측 결과는 Community MySQL 두 인스턴스의 binary log 복제이며 Aurora 장애 재현 결과는 아니다.

9. 운영 체크리스트

10. 정리

복제 오류 복구의 목표는 thread를 다시 ON으로 만드는 것이 아니라, 올바른 데이터를 가진 replica가 올바른 경계에서 계속 적용하도록 만드는 것이다. 1062와 1032는 그 전제를 점검하라는 신호다. 데이터 보정 후 원래 트랜잭션을 적용하는 것과 실행 이력만 전진시키는 스킵을 구분해야 한다.

다음 단계인 복제 정합성 비교와 replica 재구축에서는 “어느 시점의 무엇이 같아야 하는가”가 핵심이다. 이번 예제처럼 GTID가 따라잡힌 상태에서도 업무 행은 빠질 수 있으므로, 진행 상태와 데이터 검증을 분리해 설계한다.

참고 문서