카테고리 : MySQL/기술노트

Multi-source replication 개요: 사용 시나리오와 충돌 위험

MySQL 다중 소스 복제의 채널 구조, 데이터 소유권과 충돌 위험을 설명하고 실제 채널 장애 진단 및 Aurora 적용 기준을 정리한다.

저자: MySQL 기술 노트 작성: 2026.09.18 약 14분 7,860자
다운로드

여러 업무 서버의 데이터를 한 서버로 모으면 통합 조회와 백업을 단순화할 수 있다. 그러나 복제 연결이 정상이라는 사실과 데이터가 올바르게 합쳐졌다는 사실은 다르다. 서로 다른 서버의 orders.id = 1이 같은 주문인지, 서로 다른 주문인지 MySQL은 판단하지 않는다. 적용 오류 없이 한쪽 값이 덮어써지는 경우도 있다.

Multi-source replication은 여러 변경 스트림을 한 replica에 적용하는 기능이지, 데이터 의미를 해석하는 병합 엔진이나 다중 쓰기 합의 프로토콜이 아니다. 도입의 첫 질문은 채널을 몇 개 만들 수 있는지가 아니라, 각 소스가 어떤 데이터와 DDL을 소유하는지다.

이 글은 Community MySQL 8.0·8.4의 비동기 복제 구조를 설명한다. 예제 SQL과 충돌 실험은 MySQL 8.0.46에서 실행했다. Aurora는 공식 문서의 지원 버전을 별도로 구분하며, Aurora 인스턴스에서 실행한 결과로 제시하지 않는다.

1. 채널은 독립적이지만 적용 대상은 공유된다

일반적인 단일 소스 복제는 하나의 source에서 binlog를 받아 relay log에 기록하고, applier가 이를 실행한다. 다중 소스 복제에서는 이 경로가 소스별 채널로 나뉜다. 채널에는 연결 정보, 수신 위치, relay log, 적용 상태가 있으며 운영 명령도 채널을 지정해 수행할 수 있다.

flowchart LR
    A["소스 A: 업무 영역 A"] --> AR["alpha 채널: 수신과 relay log"]
    B["소스 B: 업무 영역 B"] --> BR["beta 채널: 수신과 relay log"]
    AR --> AA["alpha coordinator와 workers"]
    BR --> BA["beta coordinator와 workers"]
    AA --> T["공유 InnoDB 테이블과 인덱스"]
    BA --> T
    T --> Q["통합 조회: 소스별 적용 경계 확인"]

여기에는 두 종류의 경계가 존재한다.

  • 채널별 경계: 수신 지연, relay log 적체, applier 오류, 재시작 위치는 채널마다 다르다. 한 채널이 멈춰도 다른 채널은 계속 적용할 수 있다.
  • 서버 공통 경계: CPU, Buffer Pool, redo 쓰기, 스토리지 대역폭, 행 잠금, metadata lock은 공유한다. 채널이 나뉘었다고 자원이 격리되지는 않는다.

병렬 적용을 활성화하면 replica_parallel_workers 설정이 각 채널에 적용된다. 이 값은 서버 전체 worker 예산을 채널 수로 나눠 주는 설정이 아니다. 병렬 적용 채널마다 coordinator와 설정된 수의 worker가 생기며, receiver는 별도다. 채널 추가와 worker 증설을 동시에 진행하면 메모리 사용과 저장 장치 경합이 함께 커질 수 있다. Community MySQL에서는 채널별로 서로 다른 worker 수를 지정하지 못한다.

하나의 replica에서 동일한 source를 향해 채널을 여러 개 만드는 방식도 사용하지 않는다. 공식 문서는 다중 소스 replica의 각 채널이 서로 다른 source를 가리켜야 한다고 규정한다. 토폴로지 내 server_id와 각 서버의 server_uuid도 충돌하지 않아야 한다.

GTID가 보장하는 것과 보장하지 않는 것

GTID는 트랜잭션의 식별자다. 서로 다른 서버에서 발생한 두 트랜잭션이 같은 PK를 수정해도 GTID가 다르면 둘 다 적용 대상이다. GTID 자동 위치 지정은 재연결과 중복 트랜잭션 판별을 돕지만, 업무적으로 같은 주문의 중복 생성이나 수정 충돌을 해결하지 않는다.

반대로 동일한 GTID가 여러 경로로 들어오는 diamond 토폴로지는 동일 트랜잭션의 중복 적용 방지 문제다. 이 경우 채널별 필터가 서로 다르면 먼저 처리한 경로의 필터 결과가 실제 데이터에 영향을 줄 수 있다. GTID 기반 diamond 구성은 모든 경로에서 필터가 일관적인지까지 검토해야 한다.

2. 사용 시나리오는 데이터 소유권으로 평가한다

시나리오 적합한 조건 먼저 확인할 위험
업무별 DB의 통합 조회 소스마다 별도 schema 또는 table을 소유 소스별 지연 차이로 인한 시점 불일치
샤드 데이터를 한 테이블로 통합 모든 소스에 동일한 스키마와 충돌 없는 키 체계 존재 PK·UNIQUE 충돌, 공통 DDL의 중복 적용
여러 소스의 백업 집약 소스별 복제 경계와 별도 복구 경로를 보존 삭제·오염도 전파되며 전역 일관 시점은 자동 생성되지 않음
마이그레이션 중 통합 대상 유지 초기 적재와 이후 변경 경계가 소스별로 연결됨 dump 덮어쓰기, GTID 누락·과다 등록, binlog 만료

가장 단순한 구조는 소스 A가 sales_a만, 소스 B가 sales_b만 소유하는 방식이다. 같은 테이블명이라도 schema가 다르면 충돌 영역을 줄일 수 있다. 그 위에 통합 조회 계층을 별도로 구성한다.

같은 대상 테이블에 여러 샤드를 합치려면 소스에서부터 출처가 포함된 키를 생성하는 편이 안전하다. replica에 source_id 열을 추가한다고 row event에 출처 값이 자동으로 채워지지 않는다. 기존 소스 스키마를 바꿀 수 없거나 데이터 변환·중복 제거가 필요하면, 별도 staging과 ETL/CDC 처리를 검토한다. 네이티브 복제 필터는 변환 파이프라인이 아니다.

다음은 업무 키를 (source_id, local_id)로 정의하는 최소 예제다. 폐기 가능한 테스트 DB에서 실행하며 생성한 테이블은 마지막에 삭제한다. 이는 키 설계 예제이지, 복제 채널을 만드는 SQL은 아니다.

CREATE TABLE ms_key_demo (
    source_id SMALLINT UNSIGNED NOT NULL,
    local_id BIGINT UNSIGNED NOT NULL,
    external_ref VARCHAR(30) NOT NULL,
    amount DECIMAL(12,2) NOT NULL,
    PRIMARY KEY (source_id, local_id),
    UNIQUE KEY uq_source_ref (source_id, external_ref)
) ENGINE=InnoDB;

INSERT INTO ms_key_demo VALUES
    (1, 100, 'ORDER-100', 10.00),
    (2, 100, 'ORDER-100', 20.00);

SELECT source_id, local_id, external_ref, amount
FROM ms_key_demo
ORDER BY source_id, local_id;

DROP TABLE ms_key_demo;

실행 결과(MySQL 8.0.x):

mysql> CREATE TABLE ms_key_demo (
    ->     source_id SMALLINT UNSIGNED NOT NULL,
    ->     local_id BIGINT UNSIGNED NOT NULL,
    ->     external_ref VARCHAR(30) NOT NULL,
    ->     amount DECIMAL(12,2) NOT NULL,
    ->     PRIMARY KEY (source_id, local_id),
    ->     UNIQUE KEY uq_source_ref (source_id, external_ref)
    -> ) ENGINE=InnoDB;

Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO ms_key_demo VALUES
    ->     (1, 100, 'ORDER-100', 10.00),
    ->     (2, 100, 'ORDER-100', 20.00);

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

mysql> SELECT source_id, local_id, external_ref, amount
    -> FROM ms_key_demo
    -> ORDER BY source_id, local_id;

+-----------+----------+--------------+--------+
| source_id | local_id | external_ref | amount |
+-----------+----------+--------------+--------+
|         1 |      100 | ORDER-100    |  10.00 |
|         2 |      100 | ORDER-100    |  20.00 |
+-----------+----------+--------------+--------+
2 rows in set (0.00 sec)

mysql> DROP TABLE ms_key_demo;

Query OK, 0 rows affected (0.00 sec)

두 소스에서 local_idexternal_ref가 같아도 출처가 다르면 공존한다. 그러나 주문 참조가 전사적으로 유일해야 한다면 위의 UNIQUE 정의는 부적절하다. 키 범위를 넓혀 오류를 없애기 전에 업무상 중복을 허용해도 되는지 먼저 결정해야 한다. 참조하는 자식 테이블의 FK와 조회 조건도 같은 출처 범위를 따라야 한다.

auto_increment_incrementauto_increment_offset으로 자동 생성 번호를 분리하는 방법은 제한적인 대안이다. 명시적으로 입력한 키, 별도의 UNIQUE 값, 소스 수 변경, 복구 중 수동 데이터 적재까지 보호하지 않는다. PK만 충돌하지 않는다고 전체 데이터 모델이 안전해지지는 않는다.

3. 충돌은 오류로 드러나기도 하고 조용히 지나가기도 한다

3.1 중복 INSERT와 일치하지 않는 UPDATE·DELETE

동일 대상 테이블에 같은 PK나 UNIQUE 값을 INSERT하면 적용 중 1062가 발생할 수 있다. UPDATE·DELETE의 대상 행이 없으면 1032가 발생할 수 있다. 두 오류는 단순 장애 코드가 아니라 데이터 소유권이나 초기 적재 경계가 깨졌다는 신호일 수 있다.

복제의 무결성 제약 오류와 업무 수준의 충돌 검출은 구별해야 한다. 1062로 멈춘다는 사실은 MySQL이 올바른 병합 규칙을 제공한다는 뜻이 아니다. 어떤 행이 진실인지 결정하거나 양쪽 변경을 합치는 작업은 하지 않는다.

3.2 같은 행의 변경은 정상 적용되어도 잘못될 수 있다

ROW 형식도 모든 충돌을 오류로 바꾸지 않는다. 특히 PK로 행을 찾는 UPDATE에서는 서로 다른 source의 변경이 같은 대상 행에 차례로 적용될 수 있다. 두 채널 사이에 업무 시간 기준의 전역 순서가 보장되지 않으므로, 최종 값을 “마지막 업무 변경”으로 해석해서는 안 된다. 이 동작을 신뢰 가능한 last-write-wins 정책으로 간주하는 것도 잘못이다.

검증에서는 두 source와 하나의 replica에 같은 merge_lab.shared_rows 테이블과 id = 2, note = 'base' 행을 준비했다. ROW 형식과 binlog_row_image=MINIMAL을 사용하고, A의 변경이 replica에 적용된 것을 확인한 뒤 B의 변경을 발생시켰다.

순서 수행한 변경 확인한 replica 값
1 A에서 id = 2notefrom_alpha로 변경 from_alpha
2 B에서 같은 키의 notefrom_beta로 변경 from_beta
3 양쪽 worker 오류와 source 행을 조회 worker 오류는 모두 0, A는 from_alpha, B와 replica는 from_beta

이는 특정 행 구조와 row image 설정으로 실제 확인한 결과다. 모든 UPDATE가 조용히 성공한다는 뜻은 아니다. 중요한 사실은 오류 0만으로 충돌 부재나 소스 간 값 일치를 증명할 수 없다는 것이다. 이 실험에서는 적용 순서를 의도적으로 제어했다. 실제 독립 채널의 도착·적용 순서를 소스의 업무 발생 시각과 동일시하면 안 된다.

3.3 DDL은 행 소유권을 나눠도 충돌한다

소스마다 서로 다른 PK 범위를 사용해도 같은 테이블에 ALTER TABLE ... ADD COLUMN을 각각 실행하면 replica에는 동일 DDL이 여러 경로로 도착할 수 있다. 두 번째 적용이 실패하거나, metadata lock 때문에 다른 채널까지 지연될 수 있다. ROW binlog를 사용해도 DDL이 행 변경처럼 처리되는 것은 아니다.

공유 테이블을 운영한다면 스키마 변경의 책임 주체, 각 소스와 대상에 적용하는 순서, 복제 제외 여부를 하나의 변경 절차로 관리해야 한다. 단순히 “DDL은 A에서만 실행”하면 B의 테이블 정의가 뒤처질 수 있으므로, B의 스키마를 어떻게 맞출지도 함께 설계한다.

4. 실제 채널 장애를 관측하는 방법

두 source와 하나의 replica를 폐기용 MySQL 8.0.46 인스턴스로 구성했다. GTID 자동 위치 지정, ROW 형식, 채널 alpha·beta, 채널당 worker 2개를 사용했다. 초기 계정과 공통 테이블 준비는 업무 복제 스트림에서 제외했으며, 소스별 정상 변경은 GTID 적용 완료를 기다린 뒤 확인했다.

실험은 다음 순서로 진행했다.

  1. A의 shard_alpha.items와 B의 shard_beta.items에 같은 id = 1을 입력했다. schema가 달라 replica에서 모두 보존되었다.
  2. 앞 절의 공통 행 덮어쓰기를 실행하고 양쪽 채널이 오류 없이 적용했는지 확인했다.
  3. A가 공통 테이블에 id = 1, note = 'alpha_first'를 INSERT하고 적용 완료를 확인했다.
  4. B가 같은 테이블에 id = 1, note = 'beta_second'를 INSERT했다. B의 worker에서 1062가 발생했고 replica에는 alpha_first가 남았다.
  5. 장애 이후 A의 독립 테이블에 추가 행을 입력했다. A 채널의 적용은 계속되었다.

아래 진단 결과는 중복 INSERT로 beta의 적용이 정지한 이후에 실제 replica에서 수집했다. SQL 문법과 객체 존재는 별도의 단일 인스턴스에서도 검사했지만, 표시한 채널 행은 실제 다중 소스 실험 결과다. 복제 채널이 없는 서버에서는 이 조회들이 Empty set을 반환할 수 있다.

수신 상태만 보면 정상으로 보인다

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;

+--------------+---------------+-------------------+
| CHANNEL_NAME | SERVICE_STATE | LAST_ERROR_NUMBER |
+--------------+---------------+-------------------+
| alpha        | ON            |                 0 |
| beta         | ON            |                 0 |
+--------------+---------------+-------------------+
2 rows in set (0.02 sec)

이 테이블의 SERVICE_STATE는 receiver 상태다. applier가 멈춰도 수신은 계속될 수 있다. 따라서 두 행이 모두 ON이고 오류가 0인 것을 전체 복제 정상 판정으로 사용하면 안 된다. 장시간 적용이 멈춘 상태에서 relay log가 증가하면 디스크 용량 문제로 확대될 수 있다.

worker 상태를 채널별로 나누어 본다

SELECT CHANNEL_NAME, COUNT(*) AS workers,
       SUM(SERVICE_STATE = 'ON') AS running_workers,
       MAX(LAST_ERROR_NUMBER) AS last_error
FROM performance_schema.replication_applier_status_by_worker
GROUP BY CHANNEL_NAME
ORDER BY CHANNEL_NAME;

실행 결과(MySQL 8.0.x):

mysql> SELECT CHANNEL_NAME, COUNT(*) AS workers,
    ->        SUM(SERVICE_STATE = 'ON') AS running_workers,
    ->        MAX(LAST_ERROR_NUMBER) AS last_error
    -> FROM performance_schema.replication_applier_status_by_worker
    -> GROUP BY CHANNEL_NAME
    -> ORDER BY CHANNEL_NAME;

+--------------+---------+-----------------+------------+
| CHANNEL_NAME | workers | running_workers | last_error |
+--------------+---------+-----------------+------------+
| alpha        |       2 |               2 |          0 |
| beta         |       2 |               0 |       1062 |
+--------------+---------+-----------------+------------+
2 rows in set (0.01 sec)

beta의 worker가 정지하고 1062가 나타나는 반면 alpha는 계속 동작했다. 이 집계의 MAX(LAST_ERROR_NUMBER)는 간단한 오류 존재 신호일 뿐, 여러 worker에서 발생한 원인을 하나로 요약하지는 못한다. 실제 장애 분석에서는 해당 채널의 개별 worker와 coordinator 상태, LAST_ERROR_MESSAGE, 오류 GTID를 추가로 확인한다. 오류 필드는 과거 기록이 남을 수 있으므로 현재 서비스 상태와 오류 시각도 함께 해석한다.

병렬 worker를 사용하지 않는 구성에서는 이 집계만으로 충분하지 않다. SHOW REPLICA STATUS와 해당 버전의 applier 상태를 함께 확인한다. 운영 제어는 반드시 FOR CHANNEL로 대상을 명확히 지정하고, 모든 채널을 동시에 재시작하는 방식은 피한다.

받은 트랜잭션과 실행 집합을 대조한다

SELECT CHANNEL_NAME,
       GTID_SUBSET(RECEIVED_TRANSACTION_SET,
                   @@GLOBAL.gtid_executed) AS received_applied
FROM performance_schema.replication_connection_status
ORDER BY CHANNEL_NAME;

실행 결과(MySQL 8.0.x):

mysql> SELECT CHANNEL_NAME,
    ->        GTID_SUBSET(RECEIVED_TRANSACTION_SET,
    ->                    @@GLOBAL.gtid_executed) AS received_applied
    -> FROM performance_schema.replication_connection_status
    -> ORDER BY CHANNEL_NAME;

+--------------+------------------+
| CHANNEL_NAME | received_applied |
+--------------+------------------+
| alpha        |                1 |
| beta         |                0 |
+--------------+------------------+
2 rows in set (0.00 sec)

beta = 0은 그 채널이 받은 GTID 중 서버 실행 집합에 포함되지 않은 것이 있다는 뜻이다. 정상 운전 중에도 수신과 적용의 짧은 차이 때문에 일시적으로 0이 될 수 있으므로, 0 자체를 영구 장애로 단정하지 않는다.

반대로 alpha = 1도 완전한 최신성이나 데이터 정합성을 증명하지 않는다.

  • receiver가 아직 받지 못한 source의 최신 트랜잭션은 비교 대상에 없다.
  • 실행 집합은 서버 공통이다. 같은 GTID가 다른 경로로 실행되었을 수도 있다.
  • 필터로 제외했거나 빈 GTID로 스킵한 트랜잭션도 실행 이력과 실제 업무 행이 다를 수 있다.
  • 수신 집합이 비어 있으면 부분집합 판정은 1이 될 수 있다.

서비스가 요구하는 최신성은 소스에서 채취한 GTID 경계와의 비교, 소스별 heartbeat 또는 업무 완료 식별자, 실제 데이터 검사까지 결합해 판단한다.

5. 초기 적재와 통합 조회에도 별도 일관성 기준이 필요하다

초기 데이터와 복제 시작점을 한 쌍으로 관리한다

소스별로 일관된 데이터 스냅샷과 그에 대응하는 binlog 위치 또는 GTID 집합을 확보해야 한다. 데이터는 과거 시점인데 복제를 더 뒤에서 시작하면 변경이 빠진다. 반대로 이미 포함된 변경을 다시 적용하면 중복 오류가 날 수 있다. 이 경계는 소스마다 따로 관리한다.

여러 dump를 순서대로 import할 때는 같은 schema·table의 DROP/CREATE가 앞서 적재한 데이터를 지우지 않는지 확인한다. GTID 기반 dump의 GTID_PURGED 설정도 기계적으로 반복 적용하지 않는다. 부분 dump에 포함된 GTID 범위가 실제 적재한 데이터 범위와 항상 같은 것은 아니다. 동일 GTID 구간을 중복 등록하거나 누락된 데이터를 실행 완료로 표시하지 않도록, 복원 절차와 소스별 포함 범위를 함께 검증한다.

준비가 끝나면 Community MySQL에서는 소스마다 고유한 이름으로 CHANGE REPLICATION SOURCE TOSTART REPLICA를 구성한다. 실제 배포에는 인증·TLS·네트워크 접근·binlog 보존 기간·채널별 필터가 필요하다. 이 글은 충돌 모델과 관측을 다루므로 자격 증명이 필요한 채널 생성 명령을 범용 복사·실행 예제로 제시하지 않는다.

하나의 SELECT가 여러 소스의 동일 업무 시점을 보장하지 않는다

통합 replica에서 consistent read를 사용하면 대상 서버 안의 일관된 스냅샷은 얻을 수 있다. 하지만 그 스냅샷은 A의 최신 주문과 B의 아직 갱신되지 않은 결제 상태를 함께 포함할 수 있다. 채널별 적용 경계가 다르기 때문이다. 대상의 MVCC 일관성과 소스 간 업무 일관성은 별개의 문제다.

보고서를 완성된 집계로 공개하려면 필요한 소스별 완료 경계를 정하고, 모두 도달했는지 확인한 뒤 조회해야 한다. 그 경계 자체도 업무에서 합의한 공통 기준이어야 한다. 원천의 주문·결제 처리가 비원자적으로 수행된다면 임의의 GTID 두 개를 기다린다고 업무 원자성이 새로 생기지는 않는다.

통합 replica는 모든 소스의 범용 승격 후보도 아니다. 일부 업무 테이블만 포함하거나 필터·키 정책이 다른 서버를 원본 source의 대체 writer로 승격하면 데이터가 누락되거나 소유권이 바뀐다. 장애 복구 후보와 분석용 집약 대상의 역할을 분리하는 편이 안전하다.

6. Aurora MySQL은 버전과 제어 경로를 따로 확인한다

Aurora Replica는 같은 클러스터 볼륨을 사용하는 읽기 인스턴스다. 서로 다른 source의 binlog를 받는 multi-source replication과 같은 기능이 아니다.

2026-09-18에 확인한 AWS 공식 문서는 Aurora MySQL 8.4.8 이상에서 다중 소스 복제를 지원하며, 최대 15개 채널을 구성할 수 있다고 명시한다. 이를 Aurora MySQL 3 계열을 포함한 모든 Aurora 버전의 지원으로 확대 해석해서는 안 된다. 실제 도입 시 엔진 버전과 리전의 사용 가능 버전을 다시 확인한다.

Aurora에서 달라지는 운영 지점은 다음과 같다.

  • 외부 binlog 적용 대상은 클러스터의 writer다. 설정과 관리 프로시저도 writer에 연결해 호출한다.
  • 채널별로 mysql.rds_set_external_source_for_channel 또는 mysql.rds_set_external_source_with_auto_position_for_channel을 사용하고, mysql.rds_start_replication_for_channel로 시작한다. Community의 관리 명령을 그대로 실행할 수 있다고 가정하지 않는다.
  • 채널별 지연은 CloudWatch의 ReplicationChannelLag와 SQL 상태를 함께 본다. 정상적인 Aurora reader 지연만 확인해서 외부 채널 적용 지연을 판단하지 않는다.
  • writer failover 시 채널 설정은 공유 스토리지에 보존되고 새 writer에서 복제 스레드가 다시 시작된다. 다만 관리 연결은 새 writer로 재접속해야 한다.
  • 스냅샷 복원은 writer failover와 다르다. 공식 문서에 따르면 클러스터 스냅샷에는 다중 소스 채널 설정이 포함되지 않으므로 복원 후 채널을 다시 구성해야 한다. 해당 시작 경계와 source binlog 보존 여부도 별도로 확인한다.

Aurora가 제공하는 관리 기능이 충돌 해결을 추가하는 것은 아니다. 소유권 분리, DDL 조정, 채널별 모니터링 원칙은 동일하다. 직접 쓰기를 막는 read-only 정책 역시 클라이언트 쓰기를 제한하는 장치이지, 두 복제 채널 간 충돌을 해결하는 장치는 아니다.

7. 도입 및 장애 대응 체크리스트

구성 전에 결정할 항목

충돌이 발생했을 때의 판단 순서

  1. 채널을 특정한다. 수신 중단인지 적용 중단인지 나누고, 해당 오류 GTID·worker·테이블·키를 보존한다.
  2. 부분 최신성 노출을 통제한다. 다른 채널이 계속 적용 중이면 통합 보고서가 불균형한 시점을 보여줄 수 있다. 서비스 요구에 따라 보고서 공개를 보류하거나 조회를 제한한다.
  3. 정상 데이터의 기준을 정한다. source와 대상 행, 같은 트랜잭션의 다른 변경, 초기 적재와 필터 이력을 확인한다.
  4. 복구 방식을 선택한다. 잘못된 대상 행의 통제된 보정, 영향 테이블 재동기화, 전체 재구축 중 데이터 누락이 없는 방법을 고른다. 보정 중 다른 채널이 같은 범위를 쓰는지도 확인한다.
  5. 스킵을 해결책으로 일반화하지 않는다. 빈 GTID 커밋 등으로 건너뛰면 오류가 사라져도 해당 트랜잭션의 다른 정상 변경까지 누락될 수 있다.
  6. 원인을 고친 뒤 검증한다. GTID 추적뿐 아니라 행·업무 건수·참조 무결성을 확인하고, 채널 지연과 서비스 공개 조건을 다시 평가한다.

정리

다중 소스 복제의 장점은 독립된 변경 스트림을 하나의 조회·백업 대상으로 모을 수 있다는 점이다. 그 대가는 데이터 소유권, 초기 적재 경계, DDL 순서, 채널별 최신성을 명시적으로 관리해야 한다는 것이다.

도입 판단은 “복제가 시작되는가”가 아니라 “서로 다른 변경이 같은 대상에 도달했을 때 의미가 보존되는가”를 기준으로 내려야 한다. 이 기준을 세운 뒤에야 채널별 필터, GTID 복구 경계, 집약 대상의 백업·복원 절차를 안전하게 연결할 수 있다.

참고 자료