AWS DMS で Amazon RDS for SQL Server のデータ移行を行う
コーヒーが好きな emi です。最近はカフェインを控えています。
AWS DMS で SQL Server の移行を検証するべく、AWS 上に RDS for SQL Server を 2 台準備して移行する検証を行いました。
構成イメージ
以下の構成で検証します。Windows Server 2022 には SQL Server Management Studio(SSMS)をインストールしておき、ソースとなる DB とターゲットとなる DB に接続して中身のデータを確認するために使います。
以下のブログを参考に、VPC や RDS for SQL Server を作成済みであるとします。
ソース DB のバージョンは SQL Server 2016 13.00.6465.1.v1
、ターゲット DB のバージョンは SQL Server 2019 15.00.4440.1.v1
としています。
ソース DB には RDS for SQL Server に SQL Server Management Studio(SSMS)でデータを挿入する | DevelopersIO で設定したデータベース test-db
、スキーマ dbo
、テーブル user
が作成された状態とします。
ターゲット DB にはデータベース test-db
のみが作成された状態とします。
全体の流れはざっくり以下のようになります。
- セキュリティグループの作成
- レプリケーションサブネットグループの作成
- レプリケーションエンドポイントの作成
- レプリケーションインスタンス(プロビジョンドインスタンス)の作成
- 移行タスクの作成
- タスクの実行
- タスクの確認
1. セキュリティグループの作成
EC2 用セキュリティグループ
- bastion-ec2-sg
インバウンドルールなし
方向 | タイプ | プロトコル | ポート範囲 | 送信先 | 説明 |
---|---|---|---|---|---|
アウトバウンド | すべてのトラフィック | すべて | すべて | 0.0.0.0/0 | - |
レプリケーションインスタンス用セキュリティグループ
- dms-replication-instance-sg
インバウンドルールなし
方向 | タイプ | プロトコル | ポート範囲 | 送信先 | 説明 |
---|---|---|---|---|---|
アウトバウンド | すべてのトラフィック | すべて | すべて | 0.0.0.0/0 | - |
RDS for SQL Server 用セキュリティグループ
- source-db-sg
方向 | タイプ | プロトコル | ポート範囲 | ソース | 説明 |
---|---|---|---|---|---|
インバウンド | MSSQL | TCP | 1433 | bastion-ec2-sg | from bastion-ec2-sg |
インバウンド | MSSQL | TCP | 1433 | dms-replication-instance-sg | from dms-replication-instance-sg |
方向 | タイプ | プロトコル | ポート範囲 | 送信先 | 説明 |
---|---|---|---|---|---|
アウトバウンド | すべてのトラフィック | すべて | すべて | 0.0.0.0/0 | - |
- target-db-sg
方向 | タイプ | プロトコル | ポート範囲 | ソース | 説明 |
---|---|---|---|---|---|
インバウンド | MSSQL | TCP | 1433 | bastion-ec2-sg | from bastion-ec2-sg |
インバウンド | MSSQL | TCP | 1433 | dms-replication-instance-sg | from dms-replication-instance-sg |
方向 | タイプ | プロトコル | ポート範囲 | 送信先 | 説明 |
---|---|---|---|---|---|
アウトバウンド | すべてのトラフィック | すべて | すべて | 0.0.0.0/0 | - |
2. レプリケーションサブネットグループの作成
DMS のコンソール画面を開くと以下のようになっています。サブネットグループをクリックしてサブネットグループを作成します。
プライベートサブネットを 3 AZ 分選択しました。(スペルが間違っているのはご容赦ください)
3. レプリケーションエンドポイントの作成
作成後に気づいたのですが、レプリケーションインスタンスを先に作成した方が接続確認ができて良かったと思います。今回はエンドポイントを先に作成してしまったのでそのまま記載していきます。
[移行または複製] - [エンドポイントを作成] をクリックするか、
[開始方法] - [エンドポイントを作成] でも作成画面に進めます。
ソースエンドポイントの作成
以下のようにソースエンドポイントを作成します。
SSL モードを選択すると、SSL を使用して、DMS からソースデータベースおよびターゲットデータベースへの安全な接続を確立します。「なし」は暗号化されず、オーバーヘッドが少ないです。今回はプライベートサブネット内の RDS for SQL Server の移行検証なので、「なし」にしています。
情報タブ転記
SSL
現在、この情報は英語でしか表示できません。
Use SSL to establish the secure connection from DMS to both a source and target database.
You can choose from several SSL modes to use for your SSL certificate verification.
none
The connection is not encrypted. This option is not secure, but requires less overhead.
require
The connection is encrypted using SSL (TLS) but no CA verification is made. This option is more secure, and requires more overhead.
verify-ca
The connection is encrypted. This option is more secure, and requires more overhead. This option verifies the server certificate.
verify-full
The connection is encrypted. This option is more secure, and requires more overhead. This option verifies the server certificate and verifies that the server hostname matches the hostname attribute for the certificate.
エンドポイントの設定は「ウィザード」を選択し、他はデフォルトで進めます。レプリケーションインスタンスはまだないので、接続確認はまだできません。エンドポイントは先に作成できます。
ソース DB 向けのレプリケーションエンドポイントができました。
ターゲットエンドポイントの作成
同様にターゲット DB 向けのレプリケーションエンドポイントも作成します。
4. レプリケーションインスタンス(プロビジョンドインスタンス)の作成
さて、次はレプリケーションインスタンスを作成しようとしましたが、エンドポイント画面の上部を見ると「サーバーレス」と「プロビジョンド」の二種類が存在しています。どうもサーバーレスだとレプリケーションインスタンスが不要のようです。
プロビジョンドを選択して見ると、レプリケーションインスタンスの作成ボタンが出てきました。
「サポートされているパス」というリンクがあるので開いてみると、
以下のページに遷移しました。
ソースが SQL Server の場合、プロビジョンドモードでレプリケーションインスタンスを作成して移行する際、以下のターゲットがサポートされている、ということのようです。
サーバーレスタブも見てみます。
以下のターゲットがサポートされています。プロビジョンドと比較すると対象が少ないです。
今回の検証では、従来のプロビジョンドで、レプリケーションインスタンスを作成していきます。「インスタンスの作成」をクリックします。
今回は以下のように設定していきます。
レプリケーションインスタンスのサイジング(インスタンスクラスの選択)はドキュメントに記載があります。大まかに以下のような感じです。
- TPS(1 秒あたりのトランザクション数)/トランザクション量が高いシステムでは、メモリ不足がボトルネックになりやすいため、R 系列(メモリ最適化タイプ)を選ぶケースが多い
- T3 系のバースト型インスタンスはコスト効率が良いが、常時高負荷になるケースではコストペナルティが出る可能性があるため注意
- 最初からオーバースペックにしすぎるとコストが無駄になるため、試運転/モニタリングによる検証フェーズを組むと良い
- 処理量に応じてインスタンスをスケールアップする
「割り当てられたストレージ (GiB)」では、レプリケーションインスタンスに必要なストレージ領域を選択します。このストレージはログファイルとキャッシュされたトランザクションに使用されます。
通常は DMS が大量のストレージを消費することはありませんが、例外として以下のような場合にストレージ消費が大きくなる場合があります。
- 大規模テーブルのロード中(処理が長引くため、変更データがキャッシュされディスクに書き出される)
- キャッシュをすべてロード後に適用する設定にしている場合(フルロード完了まで変更がすべてキャッシュされる)
ほとんどの場合はデフォルトのストレージ割り当てで十分ですが、メトリクスを監視し、使用量が増えている場合はストレージを拡張すると良いです。
割り当てられたストレージ (GiB)情報タブ転記
AWS DMS uses GP2 storage volume which impacts IOPS.
DMS doesn't use a significant amount of storage. Some exceptions include the following:
Very large tables that incur a significant transaction load. Loading a large table can take some time, so cached transactions are more likely to be written to disk during a large table load.
Tasks that are configured to pause before loading cached transactions. In this case, all transactions are cached until the full load completes for all tables. With this configuration, a fair amount of storage might be consumed by cached transactions.
In most cases, the default allocation of storage is sufficient. However, it's always a good idea to pay attention to storage-related metrics. Make sure to scale up your storage if you find you are consuming more than the default allocation.
DMS のエンジンバージョンは基本は最新のもので良いです。リリースノートは以下です。
検証のためシングル AZ としています。
ネットワークは事前に作成済みの VPC を選択します。(今回は過去 DocumentDB の検証を行った際に作成した VPC をそのまま流用しています。)
作成済みのサブネットグループを設定、AZ は指定なし、セキュリティグループは作成済みのレプリケーションインスタンス用のものを設定します。
メンテナンスウィンドウを設定します。UTC であることに注意してください。自動バージョンアップグレードも「はい」にしておきます。
今回は Kerberos 認証は使わないので設定せず、レプリケーションインスタンスを作成します。
10 分程度待ち、レプリケーションインスタンスができました。
ちなみにレプリケーションインスタンスは EC2 の画面には出てきませんでした。
レプリケーションインスタンスとエンドポイントの接続を確認します。エンドポイント画面で作成済みのソース DB エンドポイントの詳細を開き、「接続のテスト」をクリックします。
「テストの実行」をクリックし、Successful となれば OK です。
同様にターゲット DB 側のエンドポイントも確認します。
もしうまくいかない場合は
- セキュリティグループが正しく設定されているか
- ターゲット DB 側にデータベースが作成されているか
などを確認してください。
5. 移行タスクの作成
[移行または複製] - [タスクを作成] をクリックします。
タスク名、作成済みのエンドポイント、プロビジョンドインスタンス(レプリケーションインスタンス)を設定します。
タスクタイプは「移行のみ」で、設定の編集モードは「ウィザード」で進めます。
ターゲットテーブル準備モードは「ターゲットのテーブルをドロップ(削除)」を設定します。
- 何もしない(Do nothing)
- 既存のテーブルをそのまま利用(構造もデータも変更しない)
- すでにターゲットにテーブルが準備されており、CDC のみで同期したい場合に適する
- ターゲット上のテーブルを削除(Drop tables on target)
- 移行開始前にターゲットテーブルを削除して再作成
- 空のテーブルから移行を始めたい場合に利用
- 切り詰め(Truncate)
- 既存のテーブル構造はそのまま残し、中身のデータだけ削除
- スキーマを保持したままデータを入れ替える用途に適する
「レプリケーションに LOB 列を含める」では、デフォルトの「制限付き LOB モード」のままにします。
LOB(Large Object)/ CLOB(Character Large Object) は、データベースで扱う非常に大きなデータを格納するための専用データ型です。通常の数値や文字列カラムよりもサイズが大きくなるのが特徴です。主に以下のようなものがあります。
- BLOB (Binary Large Object)
- 画像、動画、音声、PDF などのバイナリデータを保存するための型
- 写真ファイルや動画ファイルそのもの
- CLOB (Character Large Object)
- 小説や記事、ソースコードなどの長文テキストデータを保存するための型
- 数万~数百万文字レベルの文章
- NCLOB (National Character LOB)
- Unicode 文字(多言語テキスト)を扱える LOB
AWS DMS で大容量オブジェクト(LOB/CLOB)を移行する際は、次の3つの方法があります。
- LOB 列を含めない
- LOB カラムは移行せず無視する
- 限定 LOB モード
- 「最大 LOB サイズ」で指定した長さまでで切り捨てる
- 高速だがデータは一部欠ける可能性あり
- 完全 LOB モード
- 「LOB チャンクサイズ」で分割して全データを転送
- 完全に移行できるが処理は遅め
レプリケーションに LOB 列を含める詳細タブ転記
AWS DMS can migrate large objects, such as LOBs and CLOBs, from the source endpoint to the target endpoint:
Don't include LOB columns: AWS DMS ignores columns or fields that contain large objects (LOBs).
Limited LOB mode: AWS DMS truncates each LOB to the size defined by “Max LOB size”. (Limited LOB mode is faster than full LOB mode.)
Full Lob mode: AWS DMS fetches the LOB data in chunks as specified in "Lob Chunk Size".
Setting LOB support
データ検証は、データがソースからターゲットへ正確に移行されたことを確認する機能です。今回は設定しないのですが、また後程検証してみたいと思います。
CloudWatch ログはオンにしてみます。
ログ記録されるコンポーネントアクティビティと各アクティビティについてログ記録される情報の量を変更できます。今回はデフォルトのままとします。
高度な設定はデフォルトのままで進めます。
テーブルマッピングではウィザードを選択して「新しい選択ルールを追加」をクリックします。
以下のような入力画面になりますので、
今回は以下のように設定します。
- スキーマ名:
dbo
- スキーマテーブル名:
user
- アクション:含む
RDS for SQL Server に SQL Server Management Studio(SSMS)でデータを挿入する | DevelopersIO でデータを挿入したテーブルの情報を入れています。
移行前評価は今回はオフにしています。また後程検証してみようと思います。
移行タスクの開始は「後で手動で行う」として、タスクを作成します。
6. タスクの実行
作成したタスクを実行します。タスクのアクションから「開始」をクリックすればいいのですが、私は何度か接続エラーなどで開始後に失敗したので、「再ロード」をクリックします。
「再ロード」で進めます。
実行中になりました。
データ量が少ないからか割とすぐに終わりました。1 分かからないくらいだったと思います。
ターゲット DB にログインしてテーブルを確認すると、
SELECT * FROM [user];
▼実行結果
id name address
----------- ---------- ----------
1 Yamada Tokyo
2 Satou Chiba
3 Kinjo Okinawa
(3 行に影響しました)
完了時刻: 2025-09-25T11:28:43.8658108+09:00
無事テーブルの中身が移行されていることが確認できました。
7. タスクの確認
移行は完了しましたが、タスクの中はどうなっているか、少し見てみましょう。
タスクを開くと「詳細」タブでタスク設定を JSON で確認できます。アクションパレットというものがあるので開いてみます。
コピーができたりするのかなと思ったのですが、やり直しや検索などを行う場所のようでした。
(参考)タスク設定(JSON)
{
"Logging": {
"EnableLogging": true,
"EnableLogContext": false,
"LogComponents": [
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "TRANSFORMATION"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "SOURCE_UNLOAD"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "IO"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "TARGET_LOAD"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "PERFORMANCE"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "SOURCE_CAPTURE"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "SORTER"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "REST_SERVER"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "VALIDATOR_EXT"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "TARGET_APPLY"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "TASK_MANAGER"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "TABLES_MANAGER"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "METADATA_MANAGER"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "FILE_FACTORY"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "COMMON"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "ADDONS"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "DATA_STRUCTURE"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "COMMUNICATION"
},
{
"Severity": "LOGGER_SEVERITY_DEFAULT",
"Id": "FILE_TRANSFER"
}
],
"CloudWatchLogGroup": "dms-tasks-dms-replication-instance",
"CloudWatchLogStream": "dms-task-35FUWBLPXVCBNBSUTEHYOU5ZQY"
},
"StreamBufferSettings": {
"StreamBufferCount": 3,
"CtrlStreamBufferSizeInMB": 5,
"StreamBufferSizeInMB": 8
},
"ErrorBehavior": {
"FailOnNoTablesCaptured": true,
"ApplyErrorUpdatePolicy": "LOG_ERROR",
"FailOnTransactionConsistencyBreached": false,
"RecoverableErrorThrottlingMax": 1800,
"DataErrorEscalationPolicy": "SUSPEND_TABLE",
"ApplyErrorEscalationCount": 0,
"RecoverableErrorStopRetryAfterThrottlingMax": false,
"RecoverableErrorThrottling": true,
"ApplyErrorFailOnTruncationDdl": false,
"DataMaskingErrorPolicy": "STOP_TASK",
"DataTruncationErrorPolicy": "LOG_ERROR",
"ApplyErrorInsertPolicy": "LOG_ERROR",
"EventErrorPolicy": "IGNORE",
"ApplyErrorEscalationPolicy": "LOG_ERROR",
"RecoverableErrorCount": -1,
"DataErrorEscalationCount": 0,
"TableErrorEscalationPolicy": "STOP_TASK",
"RecoverableErrorInterval": 5,
"ApplyErrorDeletePolicy": "IGNORE_RECORD",
"TableErrorEscalationCount": 0,
"FullLoadIgnoreConflicts": true,
"DataErrorPolicy": "LOG_ERROR",
"TableErrorPolicy": "SUSPEND_TABLE"
},
"TTSettings": null,
"FullLoadSettings": {
"CommitRate": 10000,
"StopTaskCachedChangesApplied": false,
"StopTaskCachedChangesNotApplied": false,
"MaxFullLoadSubTasks": 8,
"TransactionConsistencyTimeout": 600,
"CreatePkAfterFullLoad": false,
"TargetTablePrepMode": "DROP_AND_CREATE"
},
"TargetMetadata": {
"ParallelApplyBufferSize": 0,
"ParallelApplyQueuesPerThread": 0,
"ParallelApplyThreads": 0,
"TargetSchema": "",
"InlineLobMaxSize": 0,
"ParallelLoadQueuesPerThread": 0,
"SupportLobs": true,
"LobChunkSize": 64,
"TaskRecoveryTableEnabled": false,
"ParallelLoadThreads": 0,
"LobMaxSize": 32,
"BatchApplyEnabled": false,
"FullLobMode": false,
"LimitedSizeLobMode": true,
"LoadMaxFileSize": 0,
"ParallelLoadBufferSize": 0
},
"BeforeImageSettings": null,
"ControlTablesSettings": {
"historyTimeslotInMinutes": 5,
"CommitPositionTableEnabled": false,
"HistoryTimeslotInMinutes": 5,
"StatusTableEnabled": false,
"SuspendedTablesTableEnabled": false,
"HistoryTableEnabled": false,
"ControlSchema": "",
"FullLoadExceptionTableEnabled": false
},
"LoopbackPreventionSettings": null,
"CharacterSetSettings": null,
"FailTaskWhenCleanTaskResourceFailed": false,
"ChangeProcessingTuning": {
"StatementCacheSize": 50,
"CommitTimeout": 1,
"RecoveryTimeout": -1,
"BatchApplyPreserveTransaction": true,
"BatchApplyTimeoutMin": 1,
"BatchSplitSize": 0,
"BatchApplyTimeoutMax": 30,
"MinTransactionSize": 1000,
"MemoryKeepTime": 60,
"BatchApplyMemoryLimit": 500,
"MemoryLimitTotal": 1024
},
"ChangeProcessingDdlHandlingPolicy": {
"HandleSourceTableDropped": true,
"HandleSourceTableTruncated": true,
"HandleSourceTableAltered": true
},
"PostProcessingRules": null
}
「テーブルの統計」タブでは以下のようにエラーやフルロード行などが確認できます。CSV がダウンロードできたので確認してみます。
Index,FullLoadStartTime,FullLoadEndTime,SchemaName,TableName,TableState,Elapsed load time,Inserts,Deletes,Updates,Ddls,AppliedInserts,AppliedDeletes,AppliedUpdates,AppliedDdls,FullLoadRows,FullLoadCondtnlChkFailedRows,FullLoadErrorRows,FullLoadReloaded,ValidationPendingRecords,ValidationFailedRecords,ValidationSuspendedRecords,ValidationState,ResyncState,ResyncTimestamp,ResyncRowsAttempted,ResyncRowsSucceeded,ResyncRowsFailed,ResyncProgress,ValidationStateDetails,LastUpdateTime,TotalRows
0,2025-09-25T02:07:59.235Z,2025-09-25T02:08:00.146Z,dbo,user,Table completed,< 1 s,0,0,0,0,0,0,0,0,3,0,0,false,0,0,0,Not enabled,Not enabled,ull,0,0,0,0,,2025-09-25T02:08:42.085Z,3
なるほど。横に長いです。
Logs の「Find in Logs」をクリックすると、
以下のように「モニタリング」タブに遷移しました。ログメッセージが確認できます。
画面下部に CloudWatch メトリクスも表示されました。
メトリクスをスクロールするとこんな感じです。
「テーブルマッピング」タブではテーブルのマッピングを確認できます。
{
"rules": [
{
"rule-id": 567659224,
"rule-name": "mfxd8umj-axmjpv",
"rule-type": "selection",
"rule-action": "include",
"object-locator": {
"schema-name": "dbo",
"table-name": "user"
},
"filters": []
}
]
}
「移行前評価」タブでは移行前評価が確認できますが、今回は設定しなかったので何も表示されていません。
おわりに
DMS を使って SQL Server の移行を試してみました。
今回は従来のプロビジョンド方式を使ってみましたが、サーバーレスなども試してみたいです。
本記事への質問やご要望については画面下部のお問い合わせ 「DevelopersIO について」 からご連絡ください。記事に関してお問い合わせいただけます。
参考
AWS のサービスを使ったオンプレミスからのデータベース移⾏ AWS Summit ONLINE 2020
AWS Database Migration Service 概要