Amazon Web Services ブログ

MongoDB クラスターから Amazon DynamoDB へのライブマイグレーションの実行

あるデータベースから別のデータベースへのデータ移行は、データの整合性、アプリケーションのダウンタイム、ターゲットとソースデータベース間の重要な設計の違いに関して、相当難易度が高くなる場合があります。AWS Data Migration Service (AWS DMS) は、MongoDB、Oracle、MySQL、Microsoft SQL Server などのデータベースの素早く、安全で、シームレスな AWS への移行を支援します。ソースデータベースは移行中もほとんど動作し続けるため、データベースに依存するアプリケーションのダウンタイムを最小限に抑えることができます。

この記事では、シャーディングされた MongoDB クラスターのライブデータを Amazon DynamoDB のテーブルにシームレスに移行するためのアプローチを説明します。Amazon DynamoDB は、あらゆる規模で一貫性のある 1 桁ミリ秒台のレイテンシーが必要なアプリケーション向けの、完全マネージド型、高速、スケーラブル、柔軟なクラウドデータベースサービスです。この記事で説明されているアプローチは、ほぼダウンタイム無しで移行を実施し、AWS DMS を使用してソースデータを変換し、少数のアクセスパターンを提供します。

DynamoDB と MongoDB のコンポーネント間のマッピング
移行を実施する前に、Amazon DynamoDB と MongoDB のコンポーネントを簡単に比較してみましょう。DynamoDB と MongoDB の多くのコンセプトは類似しており、どちらも JSON のようなデータを柔軟でダイナミックなスキーマでアプリケーションに保存することができますが、いくつかの点で大きく異なります。

このセクションでは、DynamoDB のコアコンセプトをいくつか説明し、DynamoDB と MongoDB のコンポーネントを比較します。MongoDB のコンセプトの詳細については、MongoDB のマニュアルを参照してください。

主要なコンポーネント
Amazon DynamoDB で用いる主要なコンポーネントは、テーブル、項目、および属性です。テーブルは項目の集合であり、各項目は属性の集合です。DynamoDB では、テーブルのアイテムを一意に識別するためプライマリキーを使用し、クエリの柔軟性を高めるためにセカンダリインデックスを使用しています。各テーブルはそれぞれに対応するインデックス、キャパシティー、スケーラビリティの設定がある分離されたユニットです。DynamoDB と MongoDB は、どちらもデータセットを項目の集合に分割することが可能です。

以下の表は DynamoDB と MongoDB の両方で一般的なコンポーネントを示しています。

MongoDB DynamoDB
コレクション テーブル
ドキュメント アイテム
フィールド 属性
セカンダリインデックス セカンダリインデックス

インデックス作成
Amazon DynamoDB でテーブルを作成する際、テーブルのプライマリキーを指定する必要があります。プライマリキーはテーブルの各アイテムを一意に識別し、2 つのアイテムが同じキーを持てないようにします。

DynamoDB は 2 種類のプライマリキーをサポートしています:

  • パーティションキー – パーティションキーとして知られる 1 つの属性によって構成されるシンプルなプライマリキーです。
  • パーティションキーとソートキー複合プライマリキーと呼ばれ、この種類のキーは 2 つの属性で構成されます。1 つ目の属性はパーティションキーであり、2 つ目の属性はソートキーです。

DynamoDB はプライマリキー値を指定することにより、テーブルのアイテムへの高速アクセスを提供します。しかし、プライマリキー以外の属性による効率的なデータへのアクセスを可能にするため、多くのアプリケーションでは 1 つ以上のセカンダリ (または代替) キーを利用可能にすることによる恩恵を受けられるかもしれません。このシナリオに対応するため、DynamoDB は以下の 2 種類のセカンダリインデックスをサポートしています。

テーブルを作成する際、それぞれがベーステーブルとレンジキーと同じパーティションキーを参照する、セカンダリインデックスを最大 5 つ作成できます。また、項目のプライマリキー以外の属性を使用したハッシュキーまたはハッシュキーおよびレンジキーを使うグローバルセカンダリインデックスも最大 5 つ作成できます。テーブルの属性の一部またはすべてを各テーブルのインデックスに射影することを選択できます。

以下の表は、DynamoDB と MongoDB のインデックス作成方法について、どのように対応しているかを示しています。

MongoDB DynamoDB
シングルフィールド パーティションキー/グローバルセカンダリインデックス
複合インデックス パーティションキーとソートキー/ローカルセカンダリインデックス/グローバルセカンダリインデックス
マルチキーインデックス 大量の属性ではなく 1 対多のテーブルを使用し、最上位の属性にローカルセカンダリインデックス/グローバルセカンダリインデックスを作成
地理空間インデックス 地理空間インデックス作成ジオライブラリ
テキストインデックス Amazon Elasticsearch Service (Amazon ES) の統合による全文検索
シャードキー パーティションキー

クエリ
Amazon DynamoDB は、テーブルからデータを取得する際に以下の 3 つのオペレーションを提供します。

  • GetItem – プライマリキーにより、テーブルから単一の項目を取得します。
  • Query – 特定のパーティションキーを持つすべての項目を取得します。さらに、ソートキーまたはテーブルのその他フィールドにフィルター条件を指定し、データのサブセットのみを取得することも可能です。
  • Scan – 指定したテーブルのすべての項目を取得します。テーブルのあらゆるフィールドに対してフィルター条件を定義する際に、より柔軟性がありますが、テーブルのすべての項目をスキャンするため、コストまたは時間がかかる場合があります。

DynamoDB では、インデックスに直接 Query オペレーションを実行します。効率的な参照をするためにインデックスを利用するスキーマを独自に設計することを常にお勧めいたします。

移行のための MongoDB データセットのサンプル
このセクションでは、米国の主要な航空会社の 1987 年以降の国内線の到着データを含むサンプルのデータセットの移行を説明します。このデータセットには、出発および到着遅延、出発および到着空港、フライト番号、予定および実際の出発/到着時刻、無着陸飛行距離などの詳細情報が含まれています。

サンプルのデータセットは以下の例で示されているように、JSON 形式で MongoDB コレクションに保存されています。このコレクションはシャード全体にデータを分散させるため、Origin および Year フィールドのシャードキーインデックスを使用して、シャーディングされています。

{  
    "_id": ObjectId("5a03f1d6c029d5af14264744"), 
    "Year": 2017, 
    "Month": 10, 
    "DayofMonth": 7, 
    "DayOfWeek": 3, 
    "DepTime": 946, 
    "CRSDepTime": 915, 
    "ArrTime": 1037, 
    "CRSArrTime": 1001, 
    "UniqueCarrier": "PS", 
    "FlightNum": 1451, 
    "ActualElapsedTime": 51, 
    "CRSElapsedTime": 46, 
    "ArrDelay": 36, 
    "DepDelay": 31, 
    "Origin": "SFO", 
    "Dest": "SAN", 
    "Distance": 192  
}

また、MongoDB コレクションの以下のフィールドに固有の複合インデックスがあります。OriginYearUniqueCarrierFlightNumDayofMonthMonthCRSDepTime (出発予定時刻)。このインデックスは空港ごとの 1 年間の遅延フライトや予定フライトの詳細などのリストを生成する一般的なシナリオのために最適化されています。

AWS DMS を使用した移行アプローチ
AWS DMS はソースの MongoDB コレクションからターゲットの DynamoDB テーブルへの移行をサポートしています。AWS DMS は MongoDB の移行を以下の 2 つのモードでサポートしています。

  • ドキュメントモード: このモードでは、AWS DMS はすべての JSON データを、ターゲットの DynamoDB テーブルの「_doc」と名付けられた単一の列に移行します。
  • テーブルモード: このモードでは、AWS DMS は MongoDB データベースの指定数のドキュメントをスキャンし、すべてのキーとその種類を含むサンプルスキーマを作成します。移行中、AWS DMS のオブジェクトマッピング機能を使用し、MongoDB の元のデータを DynamoDB の必要な構造に変換できます。

この記事では、基本的に移行のテーブルモードを扱います。

以下は MongoDB のシャードクラスターから Amazon DynamoDB へのデータフローを示すハイレベルダイアグラムです。

データベースの移行を実施するため、AWS DMS はソースの MongoDB データベースに接続し、ソースデータを読み取り、ターゲットの DynamoDB テーブルでの使用のためにデータを変換し、データを DynamoDB テーブルにロードします。

シャードコレクションの場合、MongoDB はシャードキーを使用してドキュメントをシャード全体に分配します。 シャードコレクションをシャードクラスターから移行する場合、各シャードを別々に移行する必要があります。

当社のアプローチでは、シャードごとに 1 つの AWS DMS タスクを使用して、各シャードを別々に単一の DynamoDB テーブルに移行します。

以下は、AWS DMS を使用して MongoDB のシャードクラスターから DynamoDB にデータを移行する際に関係する、高次のタスクです。

  • 移行のため MongoDB クラスターを準備します。
  • レプリケーションサーバーを作成します。
  • ソースの MongoDB エンドポイントとターゲットの DynamoDB エンドポイントを作成します。
  • MongoDB クラスターと DynamoDB 間のデータ移行のレプリケーションタスクを作成し、開始します。

移行のための MongoDB クラスターの準備
ステップ 1: ソースの MongoDB クラスターのバランサーを無効にし、 処理中のチャンクマイグレーションの完了を待ちます。これは、進行中のあるシャードから別のシャードへの移行に関係するエラーを避けるために必要です。詳細については、MongoDB のマニュアルのバランサーの無効化を参照してください。

sh.stopBalancer();
while (sh.isBalancerRunning()) {
    print(‘waiting…’);
    sleep(1000);
}
print(‘Balancer is not running…’);

ステップ 2: 各シャードのプライマリレプリカの cleanupOrphaned コマンドを実行します。これにより、移行の失敗やアプリケーションエラーで残ったすべての「orphaned document」を削除します。詳細については、MongoDB のマニュアルの cleanupOrphaned をご覧ください。

ステップ 3: MongoDB ソースに対して変更データキャプチャ (CDC) を使用するには、MongoDB オペレーションログまたは oplog を有効にします。このログは MongoDB の特別な capped コレクションであり、保存されたデータに変更を加えるすべてのオペレーションの段階的な記録を保持します。MongoDB で oplog を有効にするには、oplog を作成するレプリカセットをデプロイします。oplog とレプリカセットに関する詳細情報は、MongoDB のマニュアルを参照してください。

また、oplog、ソースデータベース、コレクションにアクセスする権限を持つ MongoDB システムルートユーザーが必要です。

レプリケーションサーバーの作成
AWS DMS は、Virtual Private Cloud (VPC) にレプリケーションインスタンスを作成します。ホワイトペーパー AWS Database Migration Service のベストプラクティスで説明されているとおり、移行タスクを実施するために十分な容量と演算能力を持つレプリケーションインスタンスを選択します。 以下のスクリーンショットのように、マルチ AZ 配置を使用した高可用性およびフェイルオーバーのサポートには、マルチ AZ オプションを選択します。レプリケーションインスタンスがソースおよびターゲットデータベースに接続する際、パブリックまたはプライベート IP アドレスを使用するかを指定できます。仮想プライベートネットワーク (VPN) 、AWS Direct Connect、VPC ピアリングを使用してレプリケーションインスタンスの VPC に接続していないネットワークにソースまたはターゲットデータベースがある場合、レプリケーションインスタンスはパブリック IP アドレスをもつはずです。

すべてのシャードソースエンドポイントからデータを移行するために 1 つレプリケーションインスタンスを作成するか、各シャードソースエンドポイントに対して 1 つずつレプリケーションインスタンスを作成できます。大量のデータを移行する際に高いパフォーマンスを実現するため、各シャードエンドポイントに対して 1 つレプリケーションインスタンスを作成することをお勧めします。

MongoDB のソースエンドポイントの作成
以下のスクリーンショットは MongoDB データベースのソースエンドポイントの作成を示しています。各シャードのプライマリレプリカに対して 1 つのソースエンドポイントを作成します。各シャードから個別にデータを移行するため、このステップは必要です。

以下の例は 3 つのシャードのソースエンドポイントを示しています。

DynamoDB のターゲットエンドポイントの作成
以下のスクリーンショットは、Amazon DynamoDB のターゲットエンドポイントの作成を示しています。AWS DMS は、移行タスク実行中に DynamoDB のターゲットエンドポイントにテーブルを作成し、いくつかの DynamoDB デフォルトパラメーター値を設定します。さらに、移行タスクに最適化された必要容量を持つ DynamoDB テーブルを必要なプライマリキーとともに事前に作成できます。

AWS DMS が引き受け、移行先の DynamoDB テーブルへのアクセスを付与するには、IAM サービスロールを作成する必要があります。

テーブルマッピングルールによるタスク作成
MongoDB と Amazon DynamoDB はどちらも動的スキーマで JSON データを保存できます。DynamoDB では、パーティションキーまたはパーティションとソートキーの組み合わせによる固有のプライマリキーが必要です。必要なプライマリキー構造を DynamoDB で作成するにはフィールドを再構成する必要があります。

パーティションキーはデータ取り込みとアクセスパターンに基づいて判断する必要があります。ベストプラクティスとして、高カーディナリティ属性の使用をお勧めします。適切な DynamoDB パーティションキーの選択方法の詳細は、ブログ記事DynamoDB におけるパーティションキー設計の手引きを参照してください。

この記事で説明されているクエリパターンの例に基づき、DynamoDB のターゲットテーブルでは複合プライマリキーの使用をお勧めします。MongoDB のシャードキーと同じフィールドの組み合わせ (OriginYear 属性) のパーティションキーと、DayofMonth (旅行日)、MonthCRSDepTime (予定出発時刻)、UniqueCarrierFlightNum 属性の組み合わせのソートキーを使った複合プライマリキーを使用してください。

以下のスクリーンショットは MongoDB と DynamoDB の間の属性マッピングを示しています。シャードコレクションについて、MongoDB はシャードキーを使用してドキュメントをシャード全体に分配します。 シャードコレクションを DynamoDB に移行するには、MongoDB の各シャードについて 1 つのタスクを作成する必要があります。

各シャードエンドポイントについて、AWS DMS コンソールで以下のオプションを選択し、AWS DMS 移行タスクを作成してください。

  • レプリケーションインスタンスを指定します。
  • ソースエンドポイントターゲットエンドポイントを指定します。
  • 移行タイプについては、データ移行中に発生する MongoDB ソースデータベースへの変更をキャプチャーするため、既存のデータを移行し、継続的な変更をレプリケートするを選択します。
  • すぐに移行タスクを開始するには、作成時にタスクを開始を選択します。
  • ターゲットテーブル準備モードについては、[Do nothing] を選択して、既存データと DynamoDB のターゲットテーブルのメタデータが影響を受けないようにします。DynamoDB のターゲットテーブルが存在しない場合、移行タスクにより新しいテーブルが作成されます。そうでなければ既存のテーブルにデータを追加します。
  • 移行タスクの追跡とデバッグのため、ログ作成の有効化を選択します。
  • テーブルマッピングについては、テーブルマッピングで [Enable JSON editing] を選択します。

以下のスクリーンショットは、コンソールの [タスクの作成] ページでのこれらの設定を示しています。[タスクの作成] でのテーブルマッピング
AWS DMS は、MongoDB ソースから Amazon DynamoDB にデータをマッピングする際にテーブルマッピングルールを使用します。DynamoDB いデータをマッピングするには、オブジェクトマッピングというテーブルマッピングルールを使用します。

DynamoDB では、AWS DMS はルールアクションの 2 つの有効なオプションとして、map-record-to-recordmap-record-to-document のみをサポートしています。オブジェクトマッピングに関するさらなる情報については、AWS Database Migration Service のターゲットとしての Amazon DynamoDB データベースの使用をご覧ください。

この例では、AWS DMS タスクを作成する際、オブジェクトマッピングのルールアクションを map-record-to-record に設定します。map-record-to-record のルールアクションは、ソースの MongoDB の各列に対して DynamoDB で属性を作成します。AWS DMS は、DynamoDB テーブル (まだ作成されていない場合)、パーティションキー、ソートキーを自動的に作成し、オブジェクトマッピングルールに基づいてあらゆる属性を除外します。

以下のテーブルマッピング JSON には 2 つのルールがあります。1 つめのルールは、MongoDB のオブジェクトロケーターの選択および特定を行う selection というルールタイプです。2 つめのルールは、ターゲットテーブル名、定義、パーティションキーとソートキーのマッピングを指定する object mapping というルールタイプです。

DynamoDB のオブジェクトマッピングの詳細については、DynamoDB にデータを移行するためのオブジェクトマッピングの使用をご覧ください。

{
	"rules": [
		{
			"rule-type": "selection",
			"rule-id": "1",
			"rule-name": "1",
			"object-locator": {
				"schema-name": "airlinedb",
				"table-name": "airline"
			},
			"rule-action": "include"
		},
		{
			"rule-type": "object-mapping",
			"rule-id": "2",
			"rule-name": "2",
			"rule-action": "map-record-to-record",
			"object-locator": {
				"schema-name": "airlinedb",
				"table-name": "airline"
			},
			"target-table-name": "airlinedata",
			"mapping-parameters": {
				"partition-key-name": "depCitybyYear",
				"sort-key-name": "depTimeByFlightNum",
				"exclude-columns": [
					"CancellationCode",
					"Diverted"
				],
				"attribute-mappings": [
					{
						"target-attribute-name": "depCitybyYear",
						"attribute-type": "scalar",
						"attribute-sub-type": "string",
						"value": "${Origin}-${Year}"
					},
					{
						"target-attribute-name": "depTimeByFlightNum",
						"attribute-type": "scalar",
						"attribute-sub-type": "string",
						"value": "${DayofMonth}-${Month}:${CRSDepTime}|${UniqueCarrier}-${FlightNum}"
					}
				]
			}
		}
	]
}

以下の画像は 3 つのシャード (前ステップで作成された 3 つのソースエンドポイントに対応) の移行タスクを示しています。移行タスクのモニタリング

AWS DMS タスクはタスクの定義に応じて、即座にまたは手動で開始できます。AWS DMS タスクは、必要なメタデータがまだ存在しない場合、それを加えたテーブルを Amazon DynamoDB に作成します。以下のスクリーンショットで示されるように、Amazon CloudWatch を使用して AWS DMS タスクの進捗をモニタリングできます。詳細については、AWS Database Migration Service タスクのモニタリングをご覧ください。また、有用な統計を提供する制御テーブルを使用して AWS DMS をモニタリングすることもできます。これらの統計情報は、現在または将来のタスクの計画や管理に使用できます。制御テーブルの設定は、[Create task] ページの [Advanced settings] リンクを使用することにより有効にできます。詳細については、制御テーブルタスク設定をご覧ください。

以下のスクリーンショットは、CloudWatch がキャプチャーしたログイベントおよびエラーを表示しています。

DynamoDB エンドポイントを使用するためのアプリケーションの修正
MongoDB から Amazon DynamoDB に移行する際、アプリケーションのコードのリファクタリングが必要です。以下の Java コードスニペットは、接続といくつかのクエリアクセスパターンに焦点を当てています。詳細とその他のアクセスパターンについては、DynamoDB および AWS SDK を使用したプログラミングをご覧ください。

データベースへの接続の確立
以下は、MongoDB および Amazon DynamoDB のデータベースへの接続の例です。

MongoDB:
以下の例は、airlinedbデータベースへの接続と、airline コレクションのハンドルを取得する方法を示しています。

String user = "root"; // ユーザー名
String authDB = "admin"; // ユーザーが定義されているデータベース名
char[] password = "passwd".toCharArray(); // 文字型配列としてのパスワード
		
MongoCredential credential = MongoCredential.createCredential(user, authDB, password);
MongoClient mongoClient = new MongoClient (new     ServerAddress("hostname",27017),Arrays.asList(credential));
		
MongoDatabase database = mongoClient.getDatabase("airlinedb");

MongoCollection<Document> collection = database.getCollection("airline");

DynamoDB:
AWS 資格情報の設定: 以下は ~/.aws/credentials という名称の AWS 資格情報ファイルの例であり、波線 (~) がホームディレクトリを表します。

[default] 
aws_access_key_id = AWS アクセスキー ID がここにきます 
aws_secret_access_key = シークレットキーがここにきます

以下の例はオレゴン州 (US_WEST_2) 地域の airlineData テーブルへの接続の確立方法を示しています。

AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
				.withRegion(Regions.US_WEST_2)
				.build(); 
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("airlineData");

クエリのパターン
以下は、MongoDB コレクションおよび DynamoDB テーブルからデータをクエリする例です。

MongoDB:
以下のコードスニペットは、MongoDB コレクションからのデータのクエリを示しています。

シナリオ 1: 2017 年のサンディエゴ国際空港におけるすべての遅発のフライト詳細情報を取得します。

MongoCollection<Document> collection = database.getCollection("airline");

Document filterCondition = new Document("Origin", "SAN")
	.append("Year", 2017)
	.append("DepDelay", new Document("$gte",0));
		         
// ドキュメントを取得し表示
Block<Document> readData = new Block<Document>() {
    @Override
    public void apply(final Document document) {
   	 	System.out.println(document.toJson());
    }
};
// フィルター条件に合致したすべてのドキュメントをクエリ
collection.find(filterCondition).forEach(readData);

// 合致するドキュメントの数を取得  
System.out.println(collection.count(filterCondition));

シナリオ 2: 2017 年 10 月 14 日午後 7 時 30 分に サンディエゴ国際空港から出発予定の PS-1451 のフライトステータスを取得します。

MongoCollection<Document> collection = database.getCollection("airline");

Document filterCondition = new Document("Origin", "SAN")
          .append("Year", 2017)
          .append("Origin", "SAN")
          .append("UniqueCarrier", "PS")
          .append("FlightNum", 1451)
          .append("Month", 10)
          .append("DayofMonth", 14)
          .append("CRSDepTime", 730);

// ドキュメントを取得し表示
Block<Document> readData = new Block<Document>() {
     @Override
     public void apply(final Document document) {
    	 	System.out.println(document.toJson());
     }
};

// フィルター条件に合致したすべてのドキュメントをクエリ
collection.find(filterCondition).forEach(readData);

DynamoDB:
以下のコードスニペットは、DynamoDB テーブルからのデータのクエリを示しています。

シナリオ 1: 2017 年のサンディエゴ国際空港におけるすべての遅発のフライト詳細情報を取得します。

Table table = dynamoDB.getTable("airlineData");
		
// クエリ条件 
QuerySpec querySpec = new QuerySpec()
        .withHashKey("depCitybyYear" , "SAN-2017")  // ハッシュキー名とその値 
        .withQueryFilters(new QueryFilter("DepDelay").ge(0));

// フィルター条件に合致したすべての項目をクエリ
ItemCollection<QueryOutcome> items = table.query(querySpec);

// 項目を取得し表示
items.forEach(System.out::println);

// 合致する項目の数を取得  
System.out.println(items.getAccumulatedItemCount());

シナリオ 2: 2017 年 10 月 14 日午後 7 時 30 分に サンディエゴ国際空港から出発予定の PS-1451 のフライトステータスを取得します。

Table table = dynamoDB.getTable("airlineData");

// プライマリキーにより項目を取得 
Item item = table.getItem("depCitybyYear", "SAN-2017", "depTimeByFlightNum", "14-10:730|PS-1451");

// 取得した項目を表示
System.out.println(item);

DynamoDB のさらなるクエリパターンについては、クエリの操作を参照してください。

結論
この記事では、AWS DMS を使用して継続的にデータキャプチャーを行った、ほぼリアルタイムの MongoDB から Amazon DynamoDB へのデータ移行について説明しました。AWS DMS は、MongoDB に保存されているシャーディングされたデータを含むデータの DynamoDB への迅速かつ安全な移行を支援します。移行プロセス中、MongoDB のソースデータベースは完全に動作し続けるため、データベースに依存するアプリケーションのダウンタイムを最小限に抑えることができます。

Amazon DynamoDB はドキュメントとキーバリューのデータ構造のどちらもサポートするため、MongoDB の JSON ドキュメントの移動は比較的単純です。MongoDB に保存されている JSON 形式のデータはあまり変更する必要はありません。

ご不明な点がございましたら、下記へコメントをお寄せください。


著者について

Gururaj S Bayari は、アマゾン ウェブ サービスのソリューションアーキテクトです。 AWS の顧客と協力してデータベースプロジェクトに関する助言や技術支援を行い、AWS を使用する場面でソリューションの価値を向上させる手助けをしています。

 

 

 

Arun Kannan はアマゾン ウェブ サービスのグローバルシステムインテグレーター (GSI) チームのパートナーソリューションアーキテクトです。 GSI と協力して、クラウド採用、ソリューション開発、移行戦略に関するアーキテクチャ面のガイダンスを提供しています。