---
title: "mysqlpump와 MySQL Shell dump utility 비교: 병렬 백업보다 복원 경로를 먼저 설계하기"
description: "mysqlpump와 MySQL Shell dump utility의 병렬 처리, 일관성, 파일 형식, 복원·재개 방식과 Aurora MySQL 적용 시 주의점을 비교한다."
tags: [ MySQL, 백업복구, 운영, DBA ]
image: "mysql-report-bg.png"
published: "2026-09-25"
updated: "2026-09-25"
author: "MySQL 기술 노트"
source_url: ""
---

# mysqlpump와 MySQL Shell dump utility 비교: 병렬 백업보다 복원 경로를 먼저 설계하기

논리 백업 도구를 고를 때 덤프 시간만 비교하면 복구 설계를 잘못하기 쉽다. 여러 스레드가 원본을 빨리 읽었더라도 복원에서는 하나의 SQL 스트림을 차례로 실행할 수 있다. 반대로 데이터를 여러 파일로 나누어 복원 병렬성을 확보하면, 파일 집합과 메타데이터·진행 상태를 함께 관리해야 한다. 선택 기준은 **얼마나 빨리 백업하는가**뿐 아니라 **어떤 산출물로, 어느 버전의 대상에, 어떤 절차로 복원하는가**다.

이 글은 Community MySQL의 논리 백업을 기준으로 설명한다. `mysqlpump`는 MySQL 8.0.34부터 deprecated이며 MySQL 8.4 배포판에서는 제거되었다. 따라서 신규 표준을 두 도구 중 고르는 문제라면 MySQL Shell dump utility를 우선 검토하고, `mysqlpump`는 기존 작업의 의존성을 파악하여 전환하는 대상으로 보는 편이 타당하다. 단순 SQL 파일이 필요한 소규모 작업에는 `mysqldump`도 여전히 별도의 선택지다.

## 1. 도구의 수명과 서버 버전을 분리한다

`mysqlpump`와 `mysqlsh`는 클라이언트 프로그램이다. 서버가 MySQL 8.0이라고 해서 그 서버에 접속하는 모든 백업 클라이언트도 8.0인 것은 아니다. 운영 서버 업그레이드와 별개로 백업 실행 호스트의 패키지를 8.4로 교체하면 `mysqlpump` 명령이 없어질 수 있다. 반대로 이전 바이너리를 보존했다고 해서 새로운 서버 버전과의 조합이 검증되는 것도 아니다.

MySQL Shell의 덤프 유틸리티는 JavaScript 모드에서 `util.dumpInstance()`, `util.dumpSchemas()`, `util.dumpTables()`로 호출한다. 복원은 `util.loadDump()`가 맡는다. 이는 `mysqlsh --sql`로 SQL 파일을 실행하는 방식과 다르다. Shell 버전이 달라지면 덤프 메타데이터와 옵션도 달라질 수 있으므로, 생성기와 로더의 버전을 함께 기록하고 호환 조합을 테스트해야 한다. 새로운 Shell이 만든 덤프를 오래된 Shell이 언제나 읽을 수 있다고 가정하지 않는다.

| 판단 항목 | mysqlpump | MySQL Shell dump utility |
|---|---|---|
| 신규 도입 관점 | 8.0에서 deprecated, 8.4에서 제거된 레거시 도구 | 신규 병렬 논리 백업·이관의 우선 검토 대상 |
| 기본 산출물 | DDL과 INSERT를 포함하는 SQL 스트림 | DDL, 데이터 청크, JSON 메타데이터 등으로 이루어진 디렉터리 |
| 덤프 병렬 단위 | 데이터베이스 객체 단위. 한 테이블을 여러 스레드로 나누어 덤프하지 않음 | 테이블 및 테이블 데이터 청크 단위 |
| 대표 복원 경로 | SQL 파일을 mysql 클라이언트로 실행 | util.loadDump가 데이터를 병렬 적재하고 DDL·인덱스 작업을 조정 |
| 압축 | 출력 압축 옵션을 별도로 선택 | 데이터 파일 압축 지원. 이 글의 실험은 zstd를 명시 |
| 실패 후 재개 | 일반 SQL 스트림 자체에 객체·청크별 복원 진행 상태가 없음 | 로더의 진행 상태 파일을 이용한 재개 지원 |
| 계정 범위 | --users 등으로 사용자 정의를 별도 선택 | instance dump와 schema/table dump의 포함 범위가 다름 |

“병렬”이라는 같은 표현을 사용해도 작업의 분해 단위와 복원 방식은 같지 않다. 기존 `mysqlpump` 파일을 SQL 문장 경계도 고려하지 않고 여러 조각으로 나누어 동시 실행하는 것은 안전한 대체 로더가 아니다. 데이터베이스 선택, 테이블 생성, INSERT, 인덱스 생성과 참조 관계의 순서를 보존해야 한다.

## 2. 테이블 병렬성과 청크 병렬성의 차이

`mysqlpump`는 큐에 배정된 객체를 여러 작업자가 처리한다. 작은 테이블이 많은 스키마에서는 동시에 할 일이 충분하지만, 데이터 대부분이 하나의 대형 테이블에 몰려 있으면 마지막 작업자 하나가 그 테이블을 끝낼 때까지 기다릴 수 있다. 스레드 수만 올려도 이 구조적 한계는 바뀌지 않는다.

MySQL Shell은 `chunking: true`일 때 테이블 데이터를 여러 파일로 나눈다. 적절한 키와 데이터 분포가 있으면 한 테이블에서도 여러 읽기 작업을 분배할 수 있고, 복원에서도 여러 작업자가 서로 다른 청크를 적재할 수 있다. 키가 없는 테이블의 분할 방식과 효율은 Shell 버전에 따라 확인해야 하며, 청크 파일이 여러 개라는 사실만으로 원본 읽기의 완벽한 부하 균등 분산까지 입증되는 것은 아니다.

`bytesPerChunk`는 압축 전 데이터 크기에 대한 목표값이다. 통계와 실행 계획 추정에 영향을 받으므로 파일마다 정확히 같은 바이트 수를 보장하지 않는다. 청크가 너무 크면 긴 작업 하나가 마지막까지 남고, 너무 작으면 파일·메타데이터 처리와 작업 스케줄링 비용이 늘어난다. 또한 `threads`는 복사 속도만이 아니라 원본 연결 수, 디스크 읽기, 네트워크, 압축 CPU, 대상 redo 생성량에 영향을 준다.

```mermaid
flowchart LR
    S["원본 InnoDB 데이터"] --> P["mysqlpump: 객체별 병렬 읽기"]
    P --> Q["SQL 스트림"]
    Q --> R["mysql: 문장 순서대로 복원"]
    S --> D["MySQL Shell: 테이블과 청크 분할"]
    D --> F["DDL · 데이터 청크 · 메타데이터"]
    F --> L["loadDump: 병렬 적재와 진행 상태 기록"]
    R --> V["행·객체·업무 규칙 검증"]
    L --> V
```

작업 시간이 짧은 작은 샘플로는 어느 도구가 빠른지 결론 내릴 수 없다. 대형 테이블 비중, 보조 인덱스 수, 대상 쓰기 처리량과 스토리지 조건을 맞춘 뒤 덤프 시간과 복원 시간을 따로 측정해야 한다. 최종 RTO에는 대상 준비, 전송, 인덱스 완성, 검증, 접속 전환까지 포함한다.

## 3. 일관성은 스레드 수가 아니라 스냅샷 경계의 문제다

### 3.1 병렬 연결의 시작 시점을 맞추는 이유

각 작업자가 임의의 시점에 스냅샷을 열면 주문 테이블에는 이전 상태가, 결제 테이블에는 이후 상태가 들어갈 수 있다. 개별 SELECT가 성공하고 각 테이블의 행 수가 자연스러워 보여도 관계 전체는 같은 시점이 아니다.

MySQL Shell의 기본값인 `consistent: true`는 처음에 전역 읽기 락 또는 사용 가능한 테이블 락을 통해 경계를 맞춘다. 각 작업자는 `REPEATABLE READ`에서 일관된 스냅샷을 시작한다. 필요한 권한이 있으면 `LOCK INSTANCE FOR BACKUP`을 획득하고 초기 읽기 락을 해제하여, InnoDB DML은 진행할 수 있게 하면서 백업을 깨뜨릴 수 있는 DDL을 제한한다.

여기서 초기 읽기 락과 backup lock은 역할이 다르다. 초기 읽기 락은 쓰기를 잠시 막을 수 있고, backup lock은 일반적인 InnoDB DML을 모두 중단하는 전역 읽기 락이 아니다. “온라인 논리 백업”을 “아무 락도 없고 애플리케이션에 영향도 없는 백업”으로 해석하면 안 된다. 장기 실행 문장과 메타데이터 락 경쟁은 초기 진입 자체를 지연시킬 수 있다.

`BACKUP_ADMIN`이 없어 backup lock을 획득하지 못하는 경우 Shell은 추가 일관성 검사를 수행한다. 공식 문서는 검사 실패 시 instance dump가 중단되며 schema/table dump에서는 작업이 계속되더라도 오류를 알릴 수 있다고 설명한다. 파일이 생성되었다는 이유만으로 성공 처리하지 말고 종료 상태, 오류 메시지와 완료 메타데이터를 함께 판단해야 한다. 권한 오류를 피하려고 `consistent: false`로 바꾸는 것은 동등한 백업이 아니라 일관성 보장을 낮추는 결정이다.

### 3.2 트랜잭션 스냅샷이 해결하지 않는 것

`mysqlpump --single-transaction`도 InnoDB의 일관된 읽기를 이용한다. 그러나 덤프 중 대상 객체의 `ALTER TABLE`, `DROP TABLE`, `RENAME TABLE`, `TRUNCATE TABLE` 같은 DDL을 허용해도 안전하다는 뜻은 아니다. MyISAM·MEMORY 등 비트랜잭션 테이블까지 InnoDB와 같은 스냅샷 보장으로 묶어서도 안 된다.

두 도구 모두 오래 유지되는 읽기 트랜잭션은 undo 정리 지연과 저장 공간 증가에 영향을 줄 수 있다. 덤프가 replica에서 실행되더라도 해당 replica의 I/O 경쟁, 적용 지연과 긴 스냅샷 수명을 관측해야 한다. “reader에서 실행하니 공짜”가 아니라 쓰기 서비스와 어느 부하를 분리했는지 명확히 하는 문제다.

## 4. SQL 파일과 덤프 디렉터리는 서로 다른 복구 자산이다

`mysqlpump`의 SQL 파일은 사람이 읽고 `mysql`로 실행할 수 있다는 장점이 있다. 다만 작업 중간에 대상 연결이 끊어지면 어느 문장까지 적용되었는지, DDL과 DML의 커밋 경계가 어디인지 별도로 판단해야 한다. 재실행 때 생기는 테이블 존재 오류나 중복 키 오류를 무조건 무시하면 완전성을 잃을 수 있다.

Shell 덤프는 디렉터리 전체가 하나의 자산이다. DDL 파일만 전달하거나 데이터 파일에서 임의의 일부를 삭제하면 온전한 복구 입력이 아니다. 파일 목록, 완료 여부, 크기·무결성, 생성한 Shell 버전과 선택 옵션을 함께 보관한다. 로더는 `LOAD DATA LOCAL INFILE`을 사용하므로 대상 서버의 `local_infile`을 적재 동안 허용해야 한다. 이는 원본 서버의 `secure_file_priv` 경로로 덤프 파일을 쓰는 작업과 다르다. 로컬 출력 경로는 **mysqlsh가 실행되는 호스트의 파일시스템**을 가리킨다.

`LOAD DATA LOCAL INFILE`은 데이터 변환 문제를 경고로 처리하며 계속할 수 있다. 종료 코드와 적재 행 수만이 아니라 경고 수, 문자셋, 잘린 값, NULL·기본값 변환과 업무 집계까지 점검해야 한다. 로컬 파일 전송을 허용하는 연결은 신뢰할 수 있는 대상에 한정하고, 복원 후 임시로 변경한 설정과 권한을 회수한다.

로더의 진행 상태는 같은 덤프와 같은 대상에 대한 재개 판단에 사용된다. `resetProgress`는 대상 데이터를 원상복구하는 옵션이 아니다. 진행 상태만 지우고 이미 적재된 객체와 데이터를 남기면 중복 또는 충돌이 생길 수 있다. 재개와 처음부터 다시 복원하는 절차를 구분하고, 완전 재시작은 별도의 빈 대상으로 수행하는 편이 안전하다. 진행 파일을 수정해 성공한 것처럼 보이게 하는 방법은 복구 절차가 아니다.

## 5. 작은 데이터로 산출물과 복원 경로를 확인한다

다음 실험은 운영 데이터와 분리된 폐기용 MySQL에서만 실행한다. 같은 InnoDB 테이블과 뷰를 두 도구로 덤프한 뒤, 실험 스키마를 비우고 각각 복원하여 모든 행의 모든 열을 대조한다. 예제 스키마명은 `dump_compare`이며 실제 업무 스키마명으로 바꾸어 실행하지 않는다.

먼저 테스트 데이터를 만든다. `payload`는 청크 분할을 관찰하기 위한 반복 문자열이며 실제 개인정보나 운영 데이터를 사용하지 않는다.

```sql
CREATE DATABASE dump_compare;
CREATE TABLE dump_compare.sample (
    id INT NOT NULL PRIMARY KEY,
    amount INT NOT NULL,
    payload VARCHAR(1024) NOT NULL
) ENGINE=InnoDB;
INSERT INTO dump_compare.sample (id, amount, payload)
WITH digits AS (
    SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2
    UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
    UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
    UNION ALL SELECT 9
)
SELECT a.n + 10*b.n + 100*c.n + 1,
       (a.n + 10*b.n + 100*c.n + 1)*10,
       REPEAT('x', 1024)
FROM digits a CROSS JOIN digits b CROSS JOIN digits c;
CREATE VIEW dump_compare.sample_summary AS
SELECT COUNT(*) AS row_count, SUM(amount) AS total_amount
FROM dump_compare.sample;
SELECT row_count, total_amount FROM dump_compare.sample_summary;
```

실행 결과(MySQL 8.0.x):

```text
mysql> CREATE DATABASE dump_compare;

Query OK, 1 row affected (0.00 sec)

mysql> CREATE TABLE dump_compare.sample (
    ->     id INT NOT NULL PRIMARY KEY,
    ->     amount INT NOT NULL,
    ->     payload VARCHAR(1024) NOT NULL
    -> ) ENGINE=InnoDB;

Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO dump_compare.sample (id, amount, payload)
    -> WITH digits AS (
    ->     SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2
    ->     UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
    ->     UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
    ->     UNION ALL SELECT 9
    -> )
    -> SELECT a.n + 10*b.n + 100*c.n + 1,
    ->        (a.n + 10*b.n + 100*c.n + 1)*10,
    ->        REPEAT('x', 1024)
    -> FROM digits a CROSS JOIN digits b CROSS JOIN digits c;

Query OK, 1000 rows affected (0.03 sec)
Records: 1000  Duplicates: 0  Warnings: 0

mysql> CREATE VIEW dump_compare.sample_summary AS
    -> SELECT COUNT(*) AS row_count, SUM(amount) AS total_amount
    -> FROM dump_compare.sample;

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT row_count, total_amount FROM dump_compare.sample_summary;

+-----------+--------------+
| row_count | total_amount |
+-----------+--------------+
|      1000 |      5005000 |
+-----------+--------------+
1 row in set (0.01 sec)
```

### 5.1 mysqlpump로 SQL 파일을 생성한다

다음은 폐기용 서버의 인증된 세션에 접속하는 환경에서 사용한 옵션 구조다. `--login-path=lab`은 독자가 사전에 등록한 테스트 접속 설정을 뜻하며, 공개 문서에 비밀번호를 넣지 않는다. 출력 디렉터리는 사전에 준비하고 실제 접속·인증 수단은 환경에 맞게 적용한다.

```bash
mysqlpump --login-path=lab \
  --single-transaction --default-parallelism=2 \
  --set-gtid-purged=OFF --databases dump_compare \
  --result-file=/backup/pump.sql

mysql --login-path=lab < /backup/pump.sql
```

복원 명령은 동일 이름의 실험 스키마를 제거한 **빈 테스트 대상**에서 수행한다. 원본 운영 서버로 향하는 접속 설정에 복원 명령을 실행하지 않는다. 이 실험의 `--set-gtid-purged=OFF`는 독립적인 데이터 왕복 검증을 위한 선택이며, 복제 초기화나 PITR에 필요한 GTID 이력을 준비했다는 의미가 아니다. 스키마 덤프와 복제 이력 처리 정책은 분리해서 설계한다.

### 5.2 MySQL Shell로 디렉터리를 생성하고 복원한다

MySQL Shell을 테스트 원본에 연결하고 JavaScript 모드에서 실행한다. `/backup/shell-dump`는 새 디렉터리 또는 비어 있는 디렉터리여야 한다.

```javascript
util.dumpSchemas(["dump_compare"], "/backup/shell-dump", {
  threads: 2,
  consistent: true,
  chunking: true,
  bytesPerChunk: "256k",
  compression: "zstd"
});
```

대상 준비 시 `local_infile`을 확인하고 필요한 경우 복원 작업 범위에서 활성화한다. 그런 다음 빈 테스트 대상에 연결한 MySQL Shell에서 실행한다.

```javascript
util.loadDump("/backup/shell-dump", {
  threads: 2,
  loadUsers: false,
  showProgress: false
});
```

이 옵션은 의도적으로 계정 복원을 요청하지 않는다. `dumpSchemas()`는 사용자·역할·권한을 포함하지 않으며, `dumpInstance()`의 사용자 포함 기본값과 다르다. 또한 instance dump에 사용자 정의가 있더라도 `loadDump()`에서 사용자 적재는 기본적으로 제외된다. 서비스 계정과 DEFINER를 어떻게 준비할 것인지 별도의 복원 항목으로 관리해야 한다.

### 5.3 실제 왕복 검증의 해석

실제 실행 환경은 MySQL Server 8.0.46, mysqlpump 8.0.46, MySQL Shell 8.0.43이었다. 공개 명령의 접속 설정과 경로는 재현을 위한 표기로 바꾸었으며, 덤프·적재 옵션은 같은 의미로 실행했다. Shell 덤프에서는 비대화형 기록을 위해 `showProgress: false`도 지정했다. 이 시험은 8.4 바이너리를 실행한 결과가 아니며, 8.4에서의 제거 여부와 일반 옵션 설명은 공식 문서에 근거한다.

덤프·복원 비교 결과:

| 검증 항목 | mysqlpump → mysql | MySQL Shell → loadDump |
|---|---|---|
| 덤프 종료 코드 | 0 | 0 |
| 복원 종료 코드 | 0 | 0 |
| 데이터 산출물 | SQL 파일 | 압축 데이터 청크 15개와 DDL·메타데이터 |
| 복원 후 테이블·뷰 | InnoDB 테이블 1개, 뷰 1개 | InnoDB 테이블 1개, 뷰 1개 |
| 실제 행 수 | 1,000 | 1,000 |
| amount 합계 | 5,005,000 | 5,005,000 |
| 기본 키 순서로 모든 행·모든 열 대조 | 원본과 완전 일치 | 원본과 완전 일치 |

Shell의 실제 출력에서 경계 조정과 적재 완료 신호만 발췌하면 다음과 같다. 서로 다른 덤프·복원 명령의 출력이며 중간 진행 메시지는 생략했다.

```text
Acquiring global read lock
Global read lock acquired
All transactions have been started
Locking instance for backup
Global read lock has been released
Running data dump using 2 threads.
Rows written: 1000

Executing view DDL - done
Loading data - done
0 warnings were reported during the load.
```

청크 수는 이번 데이터와 통계에서 관측한 값이며 `256k`로 설정하면 언제나 15개가 나온다는 뜻이 아니다. 반복 문자열의 압축률도 실제 업무 데이터의 압축률을 대표하지 않는다. 파일 분할과 정상 왕복 복원을 확인한 시험이지 처리량 벤치마크가 아니다.

이 시험은 소형 단일 서버에서 동일 스키마를 재생성한 논리 복원 검증이다. 대규모 성능 우열, 복원 중 프로세스 강제 종료 후 재개, 계정 이관, 서버 손실, GTID/PITR 및 Aurora 복구까지 검증한 것은 아니다.

## 6. 복구 후 행 수를 넘어 객체와 값까지 확인한다

다음 SQL은 테이블 엔진, 뷰와 집계를 확인한다. 정확한 행 수는 `information_schema.TABLES.TABLE_ROWS`의 InnoDB 추정치 대신 실제 SELECT를 사용한다.

```sql
SELECT TABLE_NAME, TABLE_TYPE, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'dump_compare'
ORDER BY TABLE_NAME;
SELECT row_count, total_amount FROM dump_compare.sample_summary;
SELECT MIN(OCTET_LENGTH(payload)) AS min_payload_bytes,
       MAX(OCTET_LENGTH(payload)) AS max_payload_bytes
FROM dump_compare.sample;
```

실행 결과(MySQL 8.0.x):

```text
mysql> SELECT TABLE_NAME, TABLE_TYPE, ENGINE
    -> FROM information_schema.TABLES
    -> WHERE TABLE_SCHEMA = 'dump_compare'
    -> ORDER BY TABLE_NAME;

+----------------+------------+--------+
| TABLE_NAME     | TABLE_TYPE | ENGINE |
+----------------+------------+--------+
| sample         | BASE TABLE | InnoDB |
| sample_summary | VIEW       | NULL   |
+----------------+------------+--------+
2 rows in set (0.01 sec)

mysql> SELECT row_count, total_amount FROM dump_compare.sample_summary;

+-----------+--------------+
| row_count | total_amount |
+-----------+--------------+
|      1000 |      5005000 |
+-----------+--------------+
1 row in set (0.00 sec)

mysql> SELECT MIN(OCTET_LENGTH(payload)) AS min_payload_bytes,
    ->        MAX(OCTET_LENGTH(payload)) AS max_payload_bytes
    -> FROM dump_compare.sample;

+-------------------+-------------------+
| min_payload_bytes | max_payload_bytes |
+-------------------+-------------------+
|              1024 |              1024 |
+-------------------+-------------------+
1 row in set (0.00 sec)
```

이 집계만 같다고 모든 값이 같다는 뜻은 아니다. 실제 검증에서는 기본 키 순서로 읽은 `(id, amount, payload)` 전체 행을 원본 기준값과 대조한다. 대규모 환경에서는 범위를 나눈 체크섬·업무 집계·표본 값 검증 등을 조합하고 검증 기준을 사전에 정한다. 뷰, 프로시저, 함수, 트리거와 이벤트는 객체 존재 여부뿐 아니라 권한과 실제 실행 결과도 별도 항목이다.

`DEFINER`를 일괄 제거하면 이관이 쉬워질 수 있으나 보안 의미가 바뀔 수 있다. 또한 이벤트 정의가 복원되었다고 즉시 예약 실행시켜도 되는 것은 아니다. 스케줄러 활성화와 외부 연동 작업은 데이터 검증 이후에 통제된 단계로 진행한다.

## 7. Aurora MySQL에서는 관리형 백업과 목적을 나눈다

Aurora MySQL에서도 SQL 수준의 논리 이관은 유용하지만, 클러스터의 스토리지 백업을 이 도구가 대신하는 것은 아니다. Aurora의 자동 백업과 시점 복원, 수동 스냅샷은 클러스터 단위 복구를 위한 관리형 경로다. Shell의 스키마 덤프는 선택적 이관, 환경 간 데이터 전달과 논리적 복원 검증에 알맞다. 운영 표준에는 양쪽의 목적과 보존 기간을 따로 적는다.

관리형 환경에서는 Community MySQL의 root 계정과 동일한 권한을 가정할 수 없다. 원본의 일관성 확보에 필요한 락·권한, 대상의 `local_infile` 설정 방법, 계정·DEFINER·지원하지 않는 DDL을 실제 Aurora 엔진 버전에서 확인해야 한다. 권한 부족을 만났다고 바로 일관성 검사를 끄거나 전체 권한을 부여하는 방식으로 해결하지 않는다. 또한 Shell의 `ocimds` 옵션은 MySQL HeatWave Service 대상 호환성 옵션이지 Aurora 이관을 자동으로 맞추는 옵션이 아니다.

reader endpoint를 사용하면 여러 작업자 연결이 서로 다른 Aurora replica에 배정될 가능성도 있다. 병렬 덤프의 여러 세션이 하나의 원본과 조정된 시점을 공유하도록, 사용할 인스턴스와 연결 경로를 명확히 고정해야 한다. 고정 인스턴스에서 읽는 것과 writer 최신 커밋까지 따라잡은 것은 별개의 조건이므로, 이관 경계에서 적용 지연도 확인한다. 이 글에서는 실제 Aurora 덤프·복원을 실행하지 않았다.

## 8. 전환 시 흔한 실패와 판단 기준

| 증상 또는 오해 | 점검할 원인 | 운영 판단 |
|---|---|---|
| 백업 호스트 업그레이드 후 mysqlpump 실행 실패 | 8.4 배포판에서 도구 제거 | 서버와 별개로 클라이언트 패키지 의존성을 이관 |
| 스레드를 늘려도 덤프가 빨라지지 않음 | 단일 대형 객체, 읽기·CPU·네트워크 포화 | 객체 분포와 청크 분할, 병목을 먼저 측정 |
| Shell이 초기에 오래 기다림 | 초기 읽기 락과 장기 문장, 메타데이터 락 | 획득 대기와 실제 복사 시간을 구분하고 중단 기준 마련 |
| 덤프 파일이 있는데 실패로 기록됨 | 일관성 검사 실패 또는 미완료 파일 집합 | 파일 존재보다 전체 작업의 성공 조건을 확인 |
| loadDump가 파일 적재를 거부함 | local_infile 또는 버전·메타데이터 호환성 | 대상 설정과 생성기·로더 조합을 점검 |
| 복원 완료인데 애플리케이션 오류 | 계정, DEFINER, 저장 프로그램, 경고로 인한 값 변환 | 행·객체·권한·업무 결과를 함께 확인 |
| 진행 상태 초기화 후 중복 오류 | 대상 데이터는 남았는데 재개 기록만 삭제 | 같은 작업 재개와 빈 대상으로의 재시작을 구분 |

전환 초기에는 동일한 기준 데이터로 두 도구의 덤프와 복원을 비교한다. 이후에는 운영 크기에 가까운 데이터로 복원 시간을 측정하고, 작업 중단·재개 시험을 추가한다. 이때 원본의 최대 허용 부하와 대상의 복원 성능을 별도로 정한다. 스레드를 높여 백업 시간이 줄어도 서비스 지연이나 replica lag가 증가하면 채택할 수 없는 설정일 수 있다.

## 9. 도입·복구 점검표

- [ ] mysqlpump 제거에 대비하여 백업 실행 호스트와 복원 호스트의 도구 버전을 기록했는가?
- [ ] Shell 생성기와 로더의 버전 조합을 실제 덤프 파일로 검증했는가?
- [ ] 데이터베이스·테이블·사용자·저장 프로그램의 포함 범위를 명시했는가?
- [ ] 초기 락 대기, DDL 제한과 비트랜잭션 테이블 처리 정책을 정했는가?
- [ ] 병렬 작업자가 사용할 원본 인스턴스와 접속 경로가 명확한가?
- [ ] 청크 크기와 스레드 수를 원본 부하와 대상 적재 성능 양쪽에서 측정했는가?
- [ ] 덤프 디렉터리 전체, 완료 상태와 복원 진행 상태를 구분하여 관리하는가?
- [ ] 복원용 local_infile·권한을 통제하고 작업 이후 회수하는가?
- [ ] 같은 작업 재개와 빈 대상으로의 전체 재시작 절차가 분리되어 있는가?
- [ ] 경고 수, 전체 행 값, 객체·권한과 업무 결과를 확인한 뒤 복구 완료를 선언하는가?
- [ ] Aurora에서는 관리형 백업·시점 복원과 논리 덤프의 역할을 구분했는가?

## 10. 정리

`mysqlpump`에서 MySQL Shell로의 전환은 명령어 하나를 교체하는 작업이 아니다. SQL 스트림을 실행하던 복구 경로에서 메타데이터와 청크를 해석하는 병렬 로더로 옮기고, 일관성·권한·진행 상태와 검증 절차를 다시 정의하는 작업이다. 신규 설계에서는 도구의 지원 수명과 실제 복원 가능성을 먼저 확인하고, 그 다음에 처리량을 조정한다.

논리 백업의 복원 경로를 확보한 뒤에는 물리 백업과 시점 복원이 제공하는 복구 범위와 RTO 차이를 비교해야 한다. 백업 생성 성공이 아니라 검증된 복구 경로가 운영의 최종 산출물이다.

## 참고 문서

- [MySQL 8.0: mysqlpump](https://dev.mysql.com/doc/refman/8.0/en/mysqlpump.html)
- [MySQL 8.4의 변경·제거 기능](https://dev.mysql.com/doc/refman/8.4/en/mysql-nutshell.html)
- [MySQL Shell: Instance, Schema, Table Dump Utilities](https://dev.mysql.com/doc/mysql-shell/8.4/en/mysql-shell-utilities-dump-instance-schema.html)
- [MySQL Shell: Dump Loading Utility](https://dev.mysql.com/doc/mysql-shell/8.4/en/mysql-shell-utilities-load-dump.html)
- [Aurora 백업과 복원](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Managing.Backups.html)
- [Aurora reader endpoint의 연결 분산](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Endpoints.Reader.html)
