---
title: "Replication filter의 위험성: replicate-do-db가 만드는 착시"
description: "MySQL replicate-do-db의 문장·행 기반 판정 차이와 DDL 누락, GTID 정상 착시를 재현하고 필터 변경·승격의 안전 기준을 정리한다."
tags: [ MySQL, 복제, 운영, 고가용성 ]
image: "mysql-report-bg.png"
published: "2026-09-14"
updated: "2026-09-14"
author: "MySQL 기술 노트"
source_url: ""
---

복제 필터는 데이터베이스를 잘라 내는 단순한 목록이 아니다. 어떤 이벤트를 replica에 **적용할지 판단하는 규칙**이다. `replicate-do-db=app`를 보고 “app의 변경만 복제한다”고 이해하면, 문장 기반 변경에서는 다른 데이터베이스의 행이 들어오고 정작 app의 변경은 빠질 수 있다. `ROW`로 통일해도 테이블 DDL에는 같은 함정이 남는다.

더 위험한 점은 이 누락이 복제 오류가 아닐 수 있다는 사실이다. 수신·적용 스레드가 정상이고 GTID 처리가 끝났어도, replica의 행과 스키마는 source와 다를 수 있다. 필터가 있는 replica를 전체 백업이나 무조건적인 승격 후보로 취급해서는 안 되는 이유다.

이 글은 MySQL 8.0 이상의 일반 비동기 복제를 기준으로 설명한다. 예제는 두 폐기용 MySQL 8.0 인스턴스에서 검증하며, 문장 기반 비교는 동작 원리를 드러내기 위한 실험이지 운영 환경을 `STATEMENT`로 바꾸라는 권고가 아니다. Aurora MySQL의 차이는 별도 절에서 구분한다.

## 1. 필터가 작동하는 위치부터 구분한다

source의 `binlog-do-db`와 replica의 `replicate-do-db`는 같은 위치에 있지 않다. source 측 필터는 binary log 기록 여부에 관여한다. 그 단계에서 빠진 변경은 해당 로그를 읽는 모든 downstream 소비자와 그 로그에 의존하는 복구 경로에서 얻을 수 없다. 반면 replica 측 필터는 이미 전달받은 이벤트의 적용을 선택한다.

```mermaid
flowchart LR
    A["source의 SQL 실행"] --> B["binary log 기록"]
    B --> C["receiver가 이벤트 수신"]
    C --> D["relay log"]
    D --> E{"replica 필터 판정"}
    E -->|포함| F["행 또는 DDL 적용"]
    E -->|제외| G["변경 내용 건너뜀"]
    F --> H["GTID 처리 이력 진행"]
    G --> H
    H --> I["진행 상태와 데이터 완전성은 별도 검증"]
```

따라서 replica 측 필터를 설정했다고 source의 binlog 크기나 전송량이 같은 비율로 줄어드는 것은 아니다. 적용 CPU와 대상 데이터 크기는 줄 수 있지만 receiver는 제외할 이벤트도 받아야 한다. 또한 이 필터는 보안상 데이터 반출 차단 수단이 아니다. 제외 대상 정보가 relay log에 도착할 수 있으므로 접근 통제·로그 보관·암호화 정책은 별도로 설계해야 한다.

## 2. 같은 옵션이 서로 다른 대상을 검사한다

다음 표는 **`replicate-do-db`만 설정한 경우**의 핵심 규칙이다. 다른 필터를 함께 사용하면 데이터베이스 단계 이후 테이블 단계의 판정까지 확인해야 한다.

| 처리 대상 | 데이터베이스 판정 기준 | 대표적인 함정 |
|---|---|---|
| 문장 형식으로 기록된 DML | source 세션의 기본 DB, 즉 `USE`로 선택한 DB | 완전 수식한 테이블 이름과 판정 DB가 다를 수 있음 |
| 행 형식으로 기록된 DML | 실제 변경되는 테이블이 속한 DB | 한 트랜잭션의 서로 다른 DB 변경 중 일부만 적용될 수 있음 |
| 테이블 DDL | 문장 이벤트의 기본 DB | `binlog_format=ROW`여도 다른 DB를 기본값으로 실행한 DDL 누락 가능 |
| `CREATE DATABASE`, `ALTER DATABASE`, `DROP DATABASE` | 해당 문장이 이름으로 지정한 DB를 검사하는 예외 | 일반적인 `USE` 규칙만으로 해석하면 안 됨 |

`USE app; UPDATE archive.t ...`가 문장 형식으로 기록되면 기본 DB는 app이다. 테이블이 archive에 있다는 사실만으로 필터에서 제외되지 않는다. 반대로 `USE archive; UPDATE app.t ...`는 app의 테이블을 바꿔도 제외될 수 있다. 대상 테이블을 찾지 못하면 조용한 누락 대신 SQL 오류로 복제가 멈출 수도 있다.

행 이벤트에는 변경 대상 테이블의 식별 정보가 있으므로 DML 판정은 더 직관적이다. 그러나 `MIXED`는 SQL별로 기록 형식이 달라질 수 있어 서버 변수 한 번만 조회해서 모든 이벤트의 판정 방식을 확정할 수 없다. 원인 조사에서는 실제 문제 구간의 binlog 이벤트와 당시 세션의 기본 DB를 확인해야 한다.

## 3. 두 인스턴스에서 교차 DB 변경을 재현한다

### 3.1. 실험 조건과 초기 상태

운영 서버에서 실행하지 않는다. 별도 source와 replica, 독립된 복제 채널을 준비하고 두 서버 모두 아래 실험용 DB 이름을 사용하지 않는지 확인한다. SQL은 source에서 실행하며, 두 DB와 빈 테이블이 **필터 없이 replica까지 복제된 뒤** 필터를 활성화한다. 처음부터 DB 생성이 제외되어 테이블이 없는 경우와 필터 자체의 판정 차이를 혼동하지 않기 위해서다.

실제 검증 버전은 두 서버 모두 MySQL 8.0.46이다. GTID auto-position을 사용하고 source의 기본 기록 형식은 `ROW`다. replica에는 `REPLICATE_DO_DB = (filter_lab_keep)` 하나만 적용한다. 계정 생성·접속 비밀번호와 네트워크 설정은 본문에 포함하지 않는다. 실험 후에는 전용 인스턴스와 실험 데이터를 폐기한다.

```sql
CREATE DATABASE filter_lab_keep;
CREATE DATABASE filter_lab_other;
CREATE TABLE filter_lab_keep.t (
    id INT PRIMARY KEY,
    note VARCHAR(32) NOT NULL
) ENGINE=InnoDB;
CREATE TABLE filter_lab_other.t LIKE filter_lab_keep.t;
```

실행 결과(MySQL 8.0.x):

```text
mysql> CREATE DATABASE filter_lab_keep;

Query OK, 1 row affected (0.00 sec)

mysql> CREATE DATABASE filter_lab_other;

Query OK, 1 row affected (0.00 sec)

mysql> CREATE TABLE filter_lab_keep.t (
    ->     id INT PRIMARY KEY,
    ->     note VARCHAR(32) NOT NULL
    -> ) ENGINE=InnoDB;

Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE filter_lab_other.t LIKE filter_lab_keep.t;

Query OK, 0 rows affected (0.01 sec)
```

초기 데이터가 동기화된 다음, replica에서 대상 채널의 applier를 멈추고 `CHANGE REPLICATION FILTER`로 위 규칙을 설정한 뒤 다시 시작한다. 여러 채널을 사용하는 운영 서버에서는 반드시 대상 채널과 기존 규칙을 확인해야 한다. 이 단계는 복제 토폴로지에 종속되므로 단독 DB에 복사할 일반 SQL 예제로 제시하지 않는다.

### 3.2. 문장 기반: 포함 DB의 행은 빠지고 제외 DB의 행은 들어온다

아래 명령은 **같은 source 연결에서 한 블록 전체를 실행**한다. 세션 변수와 `USE`는 다른 연결로 이어지지 않는다. 3.2~3.4의 실행 결과는 핵심 INSERT·ALTER의 성공 응답만 발췌했다. 세션 설정 응답과 DB 선택 화면은 생략했다.

```sql
SET SESSION binlog_format = 'STATEMENT';
USE filter_lab_other;
INSERT INTO filter_lab_keep.t VALUES (11, 'statement_to_keep');
USE filter_lab_keep;
INSERT INTO filter_lab_other.t VALUES (12, 'statement_to_other');
```

실행 결과(MySQL 8.0.x):

```text
mysql> INSERT INTO filter_lab_keep.t VALUES (11, 'statement_to_keep');

Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO filter_lab_other.t VALUES (12, 'statement_to_other');

Query OK, 1 row affected (0.00 sec)
```

source에서는 두 INSERT 모두 성공한다. 그러나 첫 번째 문장의 기본 DB는 `filter_lab_other`여서 id 11은 replica에서 제외된다. 두 번째 문장의 기본 DB는 `filter_lab_keep`이므로 id 12는 이름상 제외 대상인 `filter_lab_other.t`에 적용된다. “keep만 복제한다”는 해석과 정반대다.

### 3.3. 행 기반: 대상 테이블의 DB로 판정한다

```sql
SET SESSION binlog_format = 'ROW';
USE filter_lab_other;
INSERT INTO filter_lab_keep.t VALUES (21, 'row_to_keep');
USE filter_lab_keep;
INSERT INTO filter_lab_other.t VALUES (22, 'row_to_other');
```

실행 결과(MySQL 8.0.x):

```text
mysql> INSERT INTO filter_lab_keep.t VALUES (21, 'row_to_keep');

Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO filter_lab_other.t VALUES (22, 'row_to_other');

Query OK, 1 row affected (0.00 sec)
```

이번에는 기본 DB와 무관하게 keep의 id 21은 replica에 들어오고 other의 id 22는 제외된다. 이것이 ROW를 선호하는 중요한 이유지만, 업무 트랜잭션의 완전성까지 보장한다는 뜻은 아니다. source의 한 트랜잭션이 두 DB를 함께 변경했다면 replica에는 허용된 DB의 변경만 남을 수 있다. 저장 엔진의 커밋이 원자적으로 수행된다는 사실과, 원래 업무 변경이 모두 보존되었다는 사실은 다르다.

정산 원장과 주문 상태, 재고와 예약 정보처럼 함께 해석해야 하는 데이터가 필터 경계를 넘는지 먼저 살펴야 한다. FK뿐 아니라 애플리케이션 조인·배치·감사 집계도 데이터 의존성이다.

### 3.4. ROW에서도 테이블 DDL은 누락된다

```sql
SET SESSION binlog_format = 'ROW';
USE filter_lab_other;
ALTER TABLE filter_lab_keep.t
    ADD COLUMN ddl_marker INT NOT NULL DEFAULT 7;
```

실행 결과(MySQL 8.0.x):

```text
mysql> ALTER TABLE filter_lab_keep.t
    ->     ADD COLUMN ddl_marker INT NOT NULL DEFAULT 7;

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

source의 keep 테이블에는 열이 추가된다. 그러나 이 DDL은 행 이벤트로 바뀌지 않는다. 기본 DB가 other인 문장 이벤트이므로 replica에서 제외된다. ORM이나 마이그레이션 도구가 공용 관리 DB로 연결한 뒤 완전 수식한 테이블 이름으로 DDL을 수행하는 경우에도 같은 위험이 있다.

DDL 누락은 즉시 오류를 만들지 않을 수 있다. 이후 행 이벤트가 도착했을 때 열 구조 차이와 MySQL의 열 매핑·타입 호환 규칙에 따라 적용 오류가 나거나 일부 차이가 드러나지 않을 수도 있다. “다음 DML에서 반드시 멈출 테니 그때 알 수 있다”는 운영 가정은 안전하지 않다.

## 4. 스레드와 GTID가 정상이어도 실제 내용은 다르다

아래 두 진단 블록의 실행 결과는 위 실험을 완료한 **replica에서 관측한 값**이다. 복제 채널이 없는 단독 서버에서는 첫 블록이 `Empty set`을 반환할 수 있다. MySQL 8.0에서 객체와 문법을 확인한 것과 실제 복제 동작을 재현한 것을 구분해야 한다.

```sql
SELECT CHANNEL_NAME, FILTER_NAME, FILTER_RULE
FROM performance_schema.replication_applier_filters
ORDER BY CHANNEL_NAME, FILTER_NAME;
SELECT SERVICE_STATE AS receiver_state,
       LAST_ERROR_NUMBER AS receiver_error,
       GTID_SUBSET(RECEIVED_TRANSACTION_SET,
                   @@GLOBAL.gtid_executed) AS received_processed,
       (SELECT MAX(LAST_ERROR_NUMBER)
        FROM performance_schema.replication_applier_status_by_worker)
         AS worker_error
FROM performance_schema.replication_connection_status;
```

실행 결과(MySQL 8.0.x):

```text
mysql> SELECT CHANNEL_NAME, FILTER_NAME, FILTER_RULE
    -> FROM performance_schema.replication_applier_filters
    -> ORDER BY CHANNEL_NAME, FILTER_NAME;

+--------------+-----------------+-----------------+
| CHANNEL_NAME | FILTER_NAME     | FILTER_RULE     |
+--------------+-----------------+-----------------+
|              | REPLICATE_DO_DB | filter_lab_keep |
+--------------+-----------------+-----------------+
1 row in set (0.00 sec)

mysql> SELECT SERVICE_STATE AS receiver_state,
    ->        LAST_ERROR_NUMBER AS receiver_error,
    ->        GTID_SUBSET(RECEIVED_TRANSACTION_SET,
    ->                    @@GLOBAL.gtid_executed) AS received_processed,
    ->        (SELECT MAX(LAST_ERROR_NUMBER)
    ->         FROM performance_schema.replication_applier_status_by_worker)
    ->          AS worker_error
    -> FROM performance_schema.replication_connection_status;

+----------------+----------------+--------------------+--------------+
| receiver_state | receiver_error | received_processed | worker_error |
+----------------+----------------+--------------------+--------------+
| ON             |              0 |                  1 |            0 |
+----------------+----------------+--------------------+--------------+
1 row in set (0.01 sec)
```

`received_processed=1`은 수신한 GTID들이 이 서버의 처리 이력에 포함되었다는 뜻이다. 제외된 트랜잭션의 GTID도 기록되므로 행 변경이 모두 적용되었다는 뜻이 아니다. 이 쿼리는 모든 채널의 worker 오류 최댓값을 요약한다. 다중 source 운영 환경에서는 채널별 worker 상태를 별도로 조회하고, 수신 집합 자체가 source의 최신 상태를 반영하는지도 확인해야 한다.

그다음 실제 데이터와 스키마를 비교한다. 결과의 DB 이름은 표시 길이를 줄이기 위해 keep과 other로 출력했다.

```sql
SELECT 'keep' AS db_label, id, note
FROM filter_lab_keep.t
UNION ALL
SELECT 'other', id, note
FROM filter_lab_other.t
ORDER BY db_label, id;
SELECT COLUMN_NAME, COLUMN_TYPE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'filter_lab_keep'
  AND TABLE_NAME = 't'
ORDER BY ORDINAL_POSITION;
```

실행 결과(MySQL 8.0.x):

```text
mysql> SELECT 'keep' AS db_label, id, note
    -> FROM filter_lab_keep.t
    -> UNION ALL
    -> SELECT 'other', id, note
    -> FROM filter_lab_other.t
    -> ORDER BY db_label, id;

+----------+----+--------------------+
| db_label | id | note               |
+----------+----+--------------------+
| keep     | 21 | row_to_keep        |
| other    | 12 | statement_to_other |
+----------+----+--------------------+
2 rows in set (0.00 sec)

mysql> SELECT COLUMN_NAME, COLUMN_TYPE
    -> FROM information_schema.COLUMNS
    -> WHERE TABLE_SCHEMA = 'filter_lab_keep'
    ->   AND TABLE_NAME = 't'
    -> ORDER BY ORDINAL_POSITION;

+-------------+-------------+
| COLUMN_NAME | COLUMN_TYPE |
+-------------+-------------+
| id          | int         |
| note        | varchar(32) |
+-------------+-------------+
2 rows in set (0.00 sec)
```

source에는 keep의 id 11·21, other의 id 12·22가 존재한다. replica에는 keep의 id 21과 other의 id 12만 남는다. source에는 `ddl_marker` 열이 있지만 replica에는 없다. 위 결과는 지연 때문에 아직 도착하지 않은 중간 화면이 아니다. source의 해당 시점 GTID 집합이 replica에서 처리될 때까지 기다린 후 확인한 결과다.

이 실험은 모든 오류 열이 0인 상태에서도 행·스키마 차이가 유지됨을 보여준다. `Seconds_Behind_Source=0` 같은 지연 지표나 GTID 진행 상황은 **필터가 허용한 처리의 진행 상태**와 함께 해석해야 하며, 전체 데이터의 동일성을 인증하지 않는다.

## 5. 필터를 바꾸면 과거 데이터도 돌아온다는 오해

replica에서 완전히 제외한 트랜잭션도 GTID 처리 이력은 남는다. 이 때문에 GTID auto-position으로 재연결해도 이미 처리한 것으로 판단한 변경을 다시 요청하지 않는다. 필터를 해제하는 작업은 앞으로 적용할 범위를 넓히는 것이지 과거 누락분을 되감는 작업이 아니다.

같은 실험에서 필터를 해제하고 복제를 다시 진행시켜도 이전에 빠진 id 11·22와 `ddl_marker`는 자동으로 생기지 않았다. 복제를 재시작하거나 대기 시간을 늘리는 것으로 복원되지 않는다. `RESET`류 명령이나 GTID 이력을 임의로 조작해 강제 재생하려 하면, 이미 존재하는 행의 중복 적용과 더 큰 불일치를 만들 수 있다.

필터 확대·해제는 다음과 같은 **데이터 이행 작업**으로 다룬다.

1. 현재 규칙, 변경 시점의 GTID 또는 좌표, 보존된 source binlog 범위를 기록한다.
2. 새로 포함할 데이터의 일관된 스냅샷과 스키마를 확보한다. 이미 허용된 데이터와의 의존성도 확인한다.
3. 쓰기 정지 또는 검증된 일관성 유지 방식 아래에서 초기 적재와 복제 시작 경계를 맞춘다.
4. 중복·누락 여부를 검증한 뒤 새 규칙으로 전환한다. 과거 제외 범위를 정확히 복원할 수 없다면 전체 replica 재구축을 우선 검토한다.
5. 재시작 후에도 같은 규칙이 유지되는지 확인하고, 행·스키마·업무 불변식을 함께 검증한다.

`CHANGE REPLICATION FILTER`의 효과는 mysqld 재시작 후 유지되지 않는다. 런타임 상태와 영구 설정을 별도로 관리해야 한다. 또한 이 명령은 명시한 필터 유형을 교체하되 다른 유형까지 자동 정리하지 않는다. `REPLICATE_DO_DB`만 비웠다고 테이블 필터까지 모두 사라졌다고 판단하면 안 된다.

## 6. 더 나은 필터 선택과 승격 판단

테이블 경계를 명확히 표현하려면 `replicate-wild-do-table` 같은 테이블 필터를 검토할 수 있다. 다만 이를 DB 필터 위에 추가하면 앞선 DB 단계에서 제외된 이벤트가 살아나는 것은 아니다. 규칙 조합의 평가 순서를 공식 문서와 실제 SQL로 확인해야 한다.

와일드카드 `%`와 `_`는 각각 여러 문자와 한 문자에 대응한다. DB 이름의 밑줄까지 리터럴로 의도했는지, 설정 파일·SQL·관리형 서비스 파라미터를 통과하며 escape가 유지되는지 확인한다. 테이블 필터는 저장 루틴·이벤트 등 모든 DB 객체의 이행을 대신하지 않는다. DDL, 교차 DB 문장, 트리거와 업무 의존성을 포함한 검증 행렬이 필요하다.

| replica의 용도 | 판단 기준 |
|---|---|
| 특정 데이터셋의 조회·분석 | 포함 범위와 제외 의존성을 문서화하고 해당 범위의 정합성을 검증 |
| 전체 source의 장애 승격 후보 | 전체 데이터·스키마·필수 객체 보존을 증명하지 못하면 후보에서 제외 |
| 전체 백업 또는 PITR 기반 | 필터 replica의 백업만으로 source 전체 복구가 가능하다고 주장하지 않음 |
| 데이터 접근 분리 | 적용 필터 외에 계정, 네트워크, 로그 접근 통제를 별도로 설계 |
| 필터 없는 replica로 전환 | 옵션 해제가 아니라 누락분 보충 또는 재구축 계획을 수립 |

지연을 줄이기 위해 긴급히 필터를 추가하는 것은 성능 조정처럼 보여도 replica의 데이터 계약을 바꾸는 일이다. 한 번 누락한 구간을 복구해야 하므로 단순한 원상 복귀가 아니다. 처리량 문제는 worker 병렬도, 단일 대형 트랜잭션, 잠금 대기, 스토리지 병목부터 분리해 판단하는 편이 안전하다.

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

같은 Aurora 클러스터의 writer와 Aurora Replica는 공통 클러스터 스토리지를 기반으로 동작한다. 이 구조를 일반 MySQL의 binlog applier 필터와 동일시해서 “reader 하나에서 특정 DB만 제거한다”고 설계하면 안 된다.

이 글의 필터 문제는 외부 MySQL 또는 다른 Aurora 클러스터에서 **binlog 복제로 수신하는 Aurora 클러스터**의 경우에 대응한다. AWS 문서는 Aurora MySQL 버전 3의 복제 필터를 DB cluster parameter group에서 설정하는 방식을 설명하며, DDL은 `binlog_format`과 무관하게 문장으로 복제된다고 명시한다. 지원 엔진 버전, 파라미터 적용 방식과 재시작 필요 여부는 대상 클러스터 기준으로 확인한다.

Aurora의 관리형 복제 제어 절차를 Community MySQL의 관리자 명령으로 그대로 대체하지 않는다. 문서에 명시된 XA 제한과 필터 값 길이 제한도 설계 시 확인한다. 또한 Aurora 내부 복제 메시지의 binlog 정보 생략 최적화나 zero-ETL 데이터 필터는 여기서 설명한 `replicate-do-db`와 별개의 기능이다.

본문의 두 인스턴스 실험은 Community MySQL에서 실행했으며 Aurora 서비스에서 동일한 실험을 수행한 것은 아니다. Aurora 적용 전에는 실제 지원 버전과 파라미터 그룹에서 같은 교차 DB DML·DDL 검증을 반복해야 한다.

## 8. 운영 점검표

- [ ] 필터 위치가 source binlog 기록 단계인지 replica 적용 단계인지 구분했다.
- [ ] 런타임 필터와 재시작 후 적용될 영구 설정을 함께 수집했다.
- [ ] 다중 source 환경에서 채널별 규칙과 전역 규칙을 구분했다.
- [ ] 문장·행 이벤트와 테이블 DDL의 판정 기준 차이를 확인했다.
- [ ] 기본 DB가 다른 교차 DB SQL과 마이그레이션 도구의 연결 DB를 시험했다.
- [ ] DB 생성·변경·삭제의 예외와 테이블·와일드카드 필터의 평가 순서를 확인했다.
- [ ] 필터 경계를 넘는 트랜잭션, 조인, FK, 트리거, 배치 의존성을 검토했다.
- [ ] 지연·GTID 상태와 별개로 행 데이터 및 스키마 정합성을 검증했다.
- [ ] 필터 확대 시 과거 누락분의 초기 적재·시작 경계·재구축 계획이 있다.
- [ ] 필터 replica를 전체 백업이나 자동 승격 후보로 잘못 등록하지 않았다.
- [ ] Aurora에서는 클러스터 내부 복제와 외부 binlog 복제를 구분했다.

## 정리

`replicate-do-db`가 만드는 착시는 옵션 이름이 아니라 **이벤트 형식에 따른 판정 기준 차이**에서 비롯된다. ROW는 DML을 실제 테이블 기준으로 처리하게 하지만 DDL 누락과 트랜잭션 일부 적용, GTID 처리 이력과 데이터 완전성의 차이를 없애지 않는다.

복제 필터를 쓴다는 것은 replica의 데이터 범위를 별도로 정의한다는 뜻이다. 그 범위를 문서화하고 실제 행·스키마로 검증해야 한다. 이 원칙은 이후 다룰 복제 재구축, 장애 승격, 백업 복구에서 어떤 서버를 신뢰할 수 있는지 판단하는 기준이 된다.

## 참고 자료

- [MySQL 8.0: replica 옵션과 replicate-do-db](https://dev.mysql.com/doc/refman/8.0/en/replication-options-replica.html)
- [MySQL 8.0: DB 단위 복제 필터 평가](https://dev.mysql.com/doc/mysql-replication-excerpt/8.0/en/replication-rules-db-options.html)
- [MySQL 8.0: CHANGE REPLICATION FILTER](https://dev.mysql.com/doc/refman/8.0/en/change-replication-filter.html)
- [MySQL 8.0: GTID의 생명주기와 필터 처리](https://dev.mysql.com/doc/refman/8.0/en/replication-gtids-lifecycle.html)
- [Aurora MySQL: 복제 필터 구성](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Replication.Filters.html)
