チュートリアル: グローバルテーブルの作成
このセクションでは、任意の整合性モード用に設定された DynamoDB グローバルテーブルを作成するための手順を順を追って説明します。アプリケーションの要件に基づいて、マルチリージョンの結果整合性 (MREC) モードまたはマルチリージョンの強力な整合性 (MRSC) モードのいずれかを選択します。
MREC グローバルテーブルは、書き込みレイテンシーを低くし、AWS リージョン全体で結果整合性を実現します。MRSC グローバルテーブルは、MREC よりも書き込みレイテンシーがわずかに高いリージョン間で強力な整合性のある読み込みを提供します。データ整合性、レイテンシー、可用性に関するアプリケーションのニーズに最適な整合性モードを選択します。
MREC 用に設定されたグローバルテーブルの作成
このセクションでは、マルチリージョンの結果整合性 (MREC) モードでグローバルテーブルを作成する方法を示します。MREC はグローバルテーブルのデフォルトの整合性モードであり、AWS リージョン全体で非同期レプリケーションによる低レイテンシーの書き込みを提供します。1 つのリージョンの項目に加えられた変更は、通常、1 秒以内に他のすべてのリージョンにレプリケートされます。これにより、MREC は低い書き込みレイテンシーを優先し、異なるリージョンがわずかに異なるバージョンのデータを返す短い期間を許容できるアプリケーションに最適です。
DynamoDB が利用可能な任意の AWS リージョンでレプリカを含む MREC グローバルテーブルを作成し、レプリカをいつでも追加または削除できます。次の例は、複数のリージョンにレプリカを持つ MREC グローバルテーブルを作成する方法を示しています。
AWS Management Console を使用してグローバルテーブルを作成するには、次の手順に従います。以下の例では、レプリカテーブルを持つグローバルテーブルを米国およびヨーロッパに作成します。
-
AWS Management Console にサインインして DynamoDB コンソール (https://console.aws.amazon.com/dynamodb/
) を開きます。 -
この例では、ナビゲーションバーのリージョンセレクターから [米国東部 (オハイオ)] を選択します。
-
コンソールの左側のナビゲーションペインで、[テーブル] を選択します。
-
[Create Table] (テーブルの作成) を選択します。
-
[テーブルの作成] ページで、次の操作を行います。
-
[テーブル名] に
Music
と入力します。 -
[パーティションキー] に
Artist
と入力します。 -
[ソートキー] に
SongTitle
と入力します。 -
他のデフォルトの設定はそのままにして、[テーブルの作成] を選択します。
この新しいテーブルは、新しいグローバルテーブルの最初のレプリカテーブルとして機能します。これは、後で追加する他のレプリカテーブルのプロトタイプです。
-
-
テーブルがアクティブになった後、次の操作を行います。
-
テーブルのリストから [ミュージック] テーブルを選択します。
-
[グローバルテーブル] タブを選択します。
-
レプリカの作成を選択します。
-
-
[利用可能なレプリケーションリージョン] ドロップダウンリストから、[米国西部 (オレゴン) us-west-2] を選択します。
コンソールは、選択したリージョンに同一名のテーブルが存在しないことを確認します。同一名のテーブルが存在する場合は、そのリージョンで新しいレプリカテーブルを作成する前に既存のテーブルを削除する必要があります。
-
レプリカの作成を選択します。これで、米国西部 (オレゴン) us-west-2 リージョンでテーブル作成プロセスが開始されます。
[Music] テーブル (およびその他すべてのレプリカテーブル) の [グローバルテーブル] タブに、テーブルが複数のリージョンでレプリケートされたことが示されます。
-
前のステップを繰り返して別のリージョンを追加しますが、リージョンとして [欧州 (フランクフルト) eu-central-1] を選択します。
-
レプリケーションをテストするには、次の操作を行います。
-
米国東部 (オハイオ) リージョンでAWS Management Consoleを使用していることを確認します。
-
[テーブルアイテムの探索] を選択します。
-
[項目を作成] を選択します。
-
[アーティスト] に「
item_1
」、[曲名] に「Song Value 1
」と入力します。 -
[項目を作成] を選択します。
-
-
他のリージョンに切り替えてレプリケーションを確認します。
-
右上隅のリージョンセレクターから、[欧州 (フランクフルト)] を選択します。
-
[ミュージック] テーブルに、作成した項目が含まれていることを確認します。
-
[米国西部 (オレゴン)] で検証を繰り返します。
-
- CLI
-
次のコード例は、最終的な一貫性を備えたマルチリージョンレプリケーション (MREC) を使用して DynamoDB グローバルテーブルを管理する方法を示しています。
マルチリージョンレプリケーション (MREC)を備えたテーブルを作成します。
レプリカテーブルの項目を配置および取得します。
レプリカを一つずつ削除します。
テーブルを削除してクリーンアップします。
- Bash スクリプトを使用した AWS CLI
-
マルチリージョンレプリケーションを備えたテーブルを作成します。
# Step 1: Create a new table (MusicTable) in US East (Ohio), with DynamoDB Streams enabled (NEW_AND_OLD_IMAGES) aws dynamodb create-table \ --table-name MusicTable \ --attribute-definitions \ AttributeName=Artist,AttributeType=S \ AttributeName=SongTitle,AttributeType=S \ --key-schema \ AttributeName=Artist,KeyType=HASH \ AttributeName=SongTitle,KeyType=RANGE \ --billing-mode PAY_PER_REQUEST \ --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \ --region us-east-2 # Step 2: Create an identical MusicTable table in US East (N. Virginia) aws dynamodb update-table --table-name MusicTable --cli-input-json \ '{ "ReplicaUpdates": [ { "Create": { "RegionName": "us-east-1" } } ] }' \ --region us-east-2 # Step 3: Create a table in Europe (Ireland) aws dynamodb update-table --table-name MusicTable --cli-input-json \ '{ "ReplicaUpdates": [ { "Create": { "RegionName": "eu-west-1" } } ] }' \ --region us-east-2
マルチリージョンテーブルについて説明します。
# Step 4: View the list of replicas created using describe-table aws dynamodb describe-table \ --table-name MusicTable \ --region us-east-2 \ --query 'Table.{TableName:TableName,TableStatus:TableStatus,MultiRegionConsistency:MultiRegionConsistency,Replicas:Replicas[*].{Region:RegionName,Status:ReplicaStatus}}'
レプリカテーブルに項目を配置します。
# Step 5: To verify that replication is working, add a new item to the Music table in US East (Ohio) aws dynamodb put-item \ --table-name MusicTable \ --item '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \ --region us-east-2
レプリカテーブルから項目を取得します。
# Step 6: Wait for a few seconds, and then check to see whether the item has been # successfully replicated to US East (N. Virginia) and Europe (Ireland) aws dynamodb get-item \ --table-name MusicTable \ --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \ --region us-east-1 aws dynamodb get-item \ --table-name MusicTable \ --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \ --region eu-west-1
レプリカを削除します。
# Step 7: Delete the replica table in Europe (Ireland) Region aws dynamodb update-table --table-name MusicTable --cli-input-json \ '{ "ReplicaUpdates": [ { "Delete": { "RegionName": "eu-west-1" } } ] }' \ --region us-east-2 # Delete the replica table in US East (N. Virginia) Region aws dynamodb update-table --table-name MusicTable --cli-input-json \ '{ "ReplicaUpdates": [ { "Delete": { "RegionName": "us-east-1" } } ] }' \ --region us-east-2
テーブルを削除してクリーンアップします。
# Clean up: Delete the primary table aws dynamodb delete-table --table-name MusicTable --region us-east-2 echo "Global table demonstration complete."
-
API の詳細については、「AWS CLI コマンドリファレンス」の以下のトピックを参照してください。
-
- Java
-
次のコード例は、複数のリージョンにまたがるレプリカを含む DynamoDB グローバルテーブルを作成して管理する方法を示しています。
グローバルセカンダリインデックスおよび DynamoDB Streams を含むテーブルを作成します。
異なるリージョンにレプリカを追加して、グローバルテーブルを作成します。
グローバルテーブルからレプリカを削除します。
テスト項目を追加して、リージョン間のレプリケーションを検証します。
グローバルテーブル設定とレプリカのステータスについて説明します。
- SDK for Java 2.x
-
AWS SDK for Java 2.x を使用して、グローバルセカンダリインデックスおよび DynamoDB Streams を含むテーブルを作成します。
public static CreateTableResponse createTableWithGSI( final DynamoDbClient dynamoDbClient, final String tableName, final String indexName) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (indexName == null || indexName.trim().isEmpty()) { throw new IllegalArgumentException("Index name cannot be null or empty"); } try { LOGGER.info("Creating table: " + tableName + " with GSI: " + indexName); CreateTableRequest createTableRequest = CreateTableRequest.builder() .tableName(tableName) .attributeDefinitions( AttributeDefinition.builder() .attributeName("Artist") .attributeType(ScalarAttributeType.S) .build(), AttributeDefinition.builder() .attributeName("SongTitle") .attributeType(ScalarAttributeType.S) .build()) .keySchema( KeySchemaElement.builder() .attributeName("Artist") .keyType(KeyType.HASH) .build(), KeySchemaElement.builder() .attributeName("SongTitle") .keyType(KeyType.RANGE) .build()) .billingMode(BillingMode.PAY_PER_REQUEST) .globalSecondaryIndexes(GlobalSecondaryIndex.builder() .indexName(indexName) .keySchema(KeySchemaElement.builder() .attributeName("SongTitle") .keyType(KeyType.HASH) .build()) .projection( Projection.builder().projectionType(ProjectionType.ALL).build()) .build()) .streamSpecification(StreamSpecification.builder() .streamEnabled(true) .streamViewType(StreamViewType.NEW_AND_OLD_IMAGES) .build()) .build(); CreateTableResponse response = dynamoDbClient.createTable(createTableRequest); LOGGER.info("Table creation initiated. Status: " + response.tableDescription().tableStatus()); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to create table: " + tableName + " - " + e.getMessage()); throw e; } }
AWS SDK for Java 2.x を使用して、テーブルがアクティブになるまで待ちます。
public static void waitForTableActive(final DynamoDbClient dynamoDbClient, final String tableName) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } try { LOGGER.info("Waiting for table to become active: " + tableName); try (DynamoDbWaiter waiter = DynamoDbWaiter.builder().client(dynamoDbClient).build()) { DescribeTableRequest request = DescribeTableRequest.builder().tableName(tableName).build(); waiter.waitUntilTableExists(request); LOGGER.info("Table is now active: " + tableName); } } catch (DynamoDbException e) { LOGGER.severe("Failed to wait for table to become active: " + tableName + " - " + e.getMessage()); throw e; } }
AWS SDK for Java 2.x を使用して、レプリカを追加して、グローバルテーブルを作成または拡張します。
public static UpdateTableResponse addReplica( final DynamoDbClient dynamoDbClient, final String tableName, final Region replicaRegion, final String indexName, final Long readCapacity) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (replicaRegion == null) { throw new IllegalArgumentException("Replica region cannot be null"); } if (indexName == null || indexName.trim().isEmpty()) { throw new IllegalArgumentException("Index name cannot be null or empty"); } if (readCapacity == null || readCapacity <= 0) { throw new IllegalArgumentException("Read capacity must be a positive number"); } try { LOGGER.info("Adding replica in region: " + replicaRegion.id() + " for table: " + tableName); // Create a ReplicationGroupUpdate for adding a replica ReplicationGroupUpdate replicationGroupUpdate = ReplicationGroupUpdate.builder() .create(builder -> builder.regionName(replicaRegion.id()) .globalSecondaryIndexes(ReplicaGlobalSecondaryIndex.builder() .indexName(indexName) .provisionedThroughputOverride(ProvisionedThroughputOverride.builder() .readCapacityUnits(readCapacity) .build()) .build()) .build()) .build(); UpdateTableRequest updateTableRequest = UpdateTableRequest.builder() .tableName(tableName) .replicaUpdates(replicationGroupUpdate) .build(); UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest); LOGGER.info("Replica addition initiated in region: " + replicaRegion.id()); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to add replica in region: " + replicaRegion.id() + " - " + e.getMessage()); throw e; } }
AWS SDK for Java 2.x を使用してグローバルテーブルからレプリカを削除します。
public static UpdateTableResponse removeReplica( final DynamoDbClient dynamoDbClient, final String tableName, final Region replicaRegion) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (replicaRegion == null) { throw new IllegalArgumentException("Replica region cannot be null"); } try { LOGGER.info("Removing replica in region: " + replicaRegion.id() + " for table: " + tableName); // Create a ReplicationGroupUpdate for removing a replica ReplicationGroupUpdate replicationGroupUpdate = ReplicationGroupUpdate.builder() .delete(builder -> builder.regionName(replicaRegion.id()).build()) .build(); UpdateTableRequest updateTableRequest = UpdateTableRequest.builder() .tableName(tableName) .replicaUpdates(replicationGroupUpdate) .build(); UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest); LOGGER.info("Replica removal initiated in region: " + replicaRegion.id()); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to remove replica in region: " + replicaRegion.id() + " - " + e.getMessage()); throw e; } }
AWS SDK for Java 2.x を使用してテスト項目を追加して、レプリケーションを検証します。
public static PutItemResponse putTestItem( final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (artist == null || artist.trim().isEmpty()) { throw new IllegalArgumentException("Artist cannot be null or empty"); } if (songTitle == null || songTitle.trim().isEmpty()) { throw new IllegalArgumentException("Song title cannot be null or empty"); } try { LOGGER.info("Adding test item to table: " + tableName); Map<String, software.amazon.awssdk.services.dynamodb.model.AttributeValue> item = new HashMap<>(); item.put( "Artist", software.amazon.awssdk.services.dynamodb.model.AttributeValue.builder() .s(artist) .build()); item.put( "SongTitle", software.amazon.awssdk.services.dynamodb.model.AttributeValue.builder() .s(songTitle) .build()); PutItemRequest putItemRequest = PutItemRequest.builder().tableName(tableName).item(item).build(); PutItemResponse response = dynamoDbClient.putItem(putItemRequest); LOGGER.info("Test item added successfully"); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to add test item to table: " + tableName + " - " + e.getMessage()); throw e; } }
AWS SDK for Java 2.x を使用して、グローバルテーブル設定とレプリカを記述します。
public static DescribeTableResponse describeTable(final DynamoDbClient dynamoDbClient, final String tableName) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } try { LOGGER.info("Describing table: " + tableName); DescribeTableRequest request = DescribeTableRequest.builder().tableName(tableName).build(); DescribeTableResponse response = dynamoDbClient.describeTable(request); LOGGER.info("Table status: " + response.table().tableStatus()); if (response.table().replicas() != null && !response.table().replicas().isEmpty()) { LOGGER.info("Number of replicas: " + response.table().replicas().size()); response.table() .replicas() .forEach(replica -> LOGGER.info( "Replica region: " + replica.regionName() + ", Status: " + replica.replicaStatus())); } return response; } catch (ResourceNotFoundException e) { LOGGER.severe("Table not found: " + tableName + " - " + e.getMessage()); throw e; } catch (DynamoDbException e) { LOGGER.severe("Failed to describe table: " + tableName + " - " + e.getMessage()); throw e; } }
AWS SDK for Java 2.x を使用したグローバルテーブル操作の完全な例。
public static void exampleUsage(final Region sourceRegion, final Region replicaRegion) { String tableName = "Music"; String indexName = "SongTitleIndex"; Long readCapacity = 15L; // Create DynamoDB client for the source region try (DynamoDbClient dynamoDbClient = DynamoDbClient.builder().region(sourceRegion).build()) { try { // Step 1: Create the initial table with GSI and streams LOGGER.info("Step 1: Creating table in source region: " + sourceRegion.id()); createTableWithGSI(dynamoDbClient, tableName, indexName); // Step 2: Wait for table to become active LOGGER.info("Step 2: Waiting for table to become active"); waitForTableActive(dynamoDbClient, tableName); // Step 3: Add replica in destination region LOGGER.info("Step 3: Adding replica in region: " + replicaRegion.id()); addReplica(dynamoDbClient, tableName, replicaRegion, indexName, readCapacity); // Step 4: Wait a moment for replica creation to start Thread.sleep(5000); // Step 5: Describe table to view replica information LOGGER.info("Step 5: Describing table to view replicas"); describeTable(dynamoDbClient, tableName); // Step 6: Add a test item to verify replication LOGGER.info("Step 6: Adding test item to verify replication"); putTestItem(dynamoDbClient, tableName, "TestArtist", "TestSong"); LOGGER.info("Global table setup completed successfully!"); LOGGER.info("You can verify replication by checking the item in region: " + replicaRegion.id()); // Step 7: Remove replica and clean up table LOGGER.info("Step 7: Removing replica from region: " + replicaRegion.id()); removeReplica(dynamoDbClient, tableName, replicaRegion); DeleteTableResponse deleteTableResponse = dynamoDbClient.deleteTable( DeleteTableRequest.builder().tableName(tableName).build()); LOGGER.info("MREC global table demonstration completed successfully!"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Thread was interrupted", e); } catch (DynamoDbException e) { LOGGER.severe("DynamoDB operation failed: " + e.getMessage()); throw e; } } }
-
API の詳細については、『AWS SDK for Java 2.x API リファレンス』の以下のトピックを参照してください。
-
MRSC 用に設定されたグローバルテーブルの作成
このセクションでは、マルチリージョンの強力な整合性 (MRSC) グローバルテーブルを作成する方法について説明します。MRSC グローバルテーブルは、リージョン間で項目の変更を同期的にレプリケートし、レプリカに対する強力な整合性のある読み込みオペレーションが常に項目の最新バージョンを返すようにします。単一リージョンテーブルを MRSC グローバルテーブルに変換する場合は、テーブルが空であることを確認する必要があります。単一リージョンテーブルを既存の項目を持つ MRSC グローバルテーブルに変換することはサポートされていません。変換プロセス中にデータがテーブルに書き込まれないよう確認してください。
MRSC グローバルテーブルは、3 つのレプリカ、または 2 つのレプリカと 1 つの監視で設定できます。MRSC グローバルテーブルを作成するときに、レプリカとオプションの監視がデプロイされるリージョンを選択します。次の例では、米国東部 (バージニア北部) および米国東部 (オハイオ) リージョンにレプリカを持つ MRSC グローバルテーブルを作成し、米国西部 (オレゴン) リージョンに監視を配置します。
AWS Management Console を使用してグローバルテーブルを作成するには、次の手順に従います。
AWS Management Console にサインインして DynamoDB コンソール (https://console.aws.amazon.com/dynamodb/
) を開きます。 -
ナビゲーションバーのリージョンセレクターで、MRSC を使用したグローバルテーブルがサポートされているリージョン (
us-east-2
など) を選択します。 -
ナビゲーションペインで、[Tables (テーブル)] を選択します。
-
[テーブルの作成] を選択します。
-
[テーブルの作成] ページで、次の操作を行います。
-
[テーブル名] に「
Music
」と入力します。 -
[パーティションキー] に「
Artist
」と入力し、デフォルトの [文字列] タイプのままにします。 -
[ソートキー] に「
SongTitle
」と入力し、デフォルトの [文字列] タイプのままにします。 -
他のデフォルトの設定はそのままにして、[テーブルの作成] を選択します
この新しいテーブルは、新しいグローバルテーブルの最初のレプリカテーブルとして機能します。これは、後で追加する他のレプリカテーブルのプロトタイプです。
-
-
テーブルがアクティブになるまで待ってから、テーブルリストから選択します。
-
[グローバルテーブル] タブを選択して、[レプリカを作成] を選択します。
-
[レプリカの作成] ページで、次の操作を行います。
-
[マルチリージョン整合性] で、[強力な整合性] を選択します。
-
[レプリケーションリージョン 1] で、「
US East (N. Virginia) us-east-1
」を選択します。 -
[レプリケーションリージョン 2] で、「
US West (Oregon) us-west-2
」を選択します。 -
米国西部 (オレゴン) リージョンの [監視として設定する] を確認します。
-
[レプリカの作成] を選択します。
-
-
レプリカと監視の作成プロセスが完了するまで待ちます。テーブルが使用可能になると、レプリカのステータスは [アクティブ] と表示されます。
開始する前に、監視リージョンを持つ MRSC グローバルテーブルを作成するために必要なアクセス許可が IAM プリンシパルにあることを確認してください。IAM プリンシパルには CreateGlobalTableWitness
、CreateTable
、および CreateTableReplica
を呼び出す権限が必要です。
米国東部 (バージニア北部) のレプリカと米国西部 (オレゴン) の監視リージョンを使用して、DynamoDB テーブル (MusicMRSC
) を正常に作成するための IAM ポリシーの例を次に示します。
- JSON
-
-
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:UpdateTable", "dynamodb:CreateTable", "dynamodb:CreateTableReplica", "dynamodb:CreateGlobalTableWitness" ], "Resource": [ "arn:aws:dynamodb:
us-west-2:123456789123:table
/MusicMRSC
", "arn:aws:dynamodb:us-east-1:123456789123:table
/MusicMRSC
" ] } ] }
次のコード例は、マルチリージョンの強力な整合性 (MRSC) を備えた DynamoDB グローバルテーブルを作成および管理する方法を示しています。
マルチリージョンの強力な整合性を備えたテーブルを作成します。
MRSC 設定とレプリカのステータスを確認します。
即時読み取りでリージョン間の強力な整合性をテストします。
MRSC 保証を使用して条件付き書き込みを実行します。
MRSC グローバルテーブルリソースをクリーンアップします。
- Bash
-
- Bash スクリプトを使用した AWS CLI
-
マルチリージョンの強力な整合性を備えたテーブルを作成します。
# Step 1: Create a new table in us-east-2 (primary region for MRSC) # Note: Table must be empty when enabling MRSC aws dynamodb create-table \ --table-name MusicTable \ --attribute-definitions \ AttributeName=Artist,AttributeType=S \ AttributeName=SongTitle,AttributeType=S \ --key-schema \ AttributeName=Artist,KeyType=HASH \ AttributeName=SongTitle,KeyType=RANGE \ --billing-mode PAY_PER_REQUEST \ --region us-east-2 # Wait for table to become active aws dynamodb wait table-exists --table-name MusicTable --region us-east-2 # Step 2: Add replica and witness with Multi-Region Strong Consistency # MRSC requires exactly three replicas in supported regions aws dynamodb update-table \ --table-name MusicTable \ --replica-updates '[{"Create": {"RegionName": "us-east-1"}}]' \ --global-table-witness-updates '[{"Create": {"RegionName": "us-west-2"}}]' \ --multi-region-consistency STRONG \ --region us-east-2
MRSC 設定とレプリカのステータスを確認します。
# Verify the global table configuration and MRSC setting aws dynamodb describe-table \ --table-name MusicTable \ --region us-east-2 \ --query 'Table.{TableName:TableName,TableStatus:TableStatus,MultiRegionConsistency:MultiRegionConsistency,Replicas:Replicas[*],GlobalTableWitnesses:GlobalTableWitnesses[*].{Region:RegionName,Status:ReplicaStatus}}'
リージョン間の即時読み取りの強力な整合性をテストします。
# Write an item to the primary region aws dynamodb put-item \ --table-name MusicTable \ --item '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"},"Album": {"S":"The Beatles 1967-1970"},"Year": {"N":"1968"}}' \ --region us-east-2 # Read the item from replica region to verify strong consistency (cannot read or write to witness) # No wait time needed - MRSC provides immediate consistency echo "Reading from us-east-1 (immediate consistency):" aws dynamodb get-item \ --table-name MusicTable \ --key '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"}}' \ --consistent-read \ --region us-east-1
MRSC 保証を使用して条件付き書き込みを実行します。
# Perform a conditional update from a different region # This demonstrates that conditions work consistently across all regions aws dynamodb update-item \ --table-name MusicTable \ --key '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"}}' \ --update-expression "SET #rating = :rating" \ --condition-expression "attribute_exists(Artist)" \ --expression-attribute-names '{"#rating": "Rating"}' \ --expression-attribute-values '{":rating": {"N":"5"}}' \ --region us-east-1
MRSC グローバルテーブルリソースをクリーンアップします。
# Remove replica tables (must be done before deleting the primary table) aws dynamodb update-table \ --table-name MusicTable \ --replica-updates '[{"Delete": {"RegionName": "us-east-1"}}]' \ --global-table-witness-updates '[{"Delete": {"RegionName": "us-west-2"}}]' \ --region us-east-2 # Wait for replicas to be deleted echo "Waiting for replicas to be deleted..." sleep 30 # Delete the primary table aws dynamodb delete-table \ --table-name MusicTable \ --region us-east-2
-
API の詳細については、「AWS CLI コマンドリファレンス」の以下のトピックを参照してください。
-
- Java
-
- SDK for Java 2.x
-
MRSC 変換の準備ができたリージョンテーブルを AWS SDK for Java 2.x を使用して作成します。
public static CreateTableResponse createRegionalTable(final DynamoDbClient dynamoDbClient, final String tableName) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } try { LOGGER.info("Creating regional table: " + tableName + " (must be empty for MRSC)"); CreateTableRequest createTableRequest = CreateTableRequest.builder() .tableName(tableName) .attributeDefinitions( AttributeDefinition.builder() .attributeName("Artist") .attributeType(ScalarAttributeType.S) .build(), AttributeDefinition.builder() .attributeName("SongTitle") .attributeType(ScalarAttributeType.S) .build()) .keySchema( KeySchemaElement.builder() .attributeName("Artist") .keyType(KeyType.HASH) .build(), KeySchemaElement.builder() .attributeName("SongTitle") .keyType(KeyType.RANGE) .build()) .billingMode(BillingMode.PAY_PER_REQUEST) .build(); CreateTableResponse response = dynamoDbClient.createTable(createTableRequest); LOGGER.info("Regional table creation initiated. Status: " + response.tableDescription().tableStatus()); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to create regional table: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to create regional table: " + tableName) .cause(e) .build(); } }
AWS SDK for Java 2.x を使用して、リージョンテーブルをレプリカと監視を備えた MRSC に変換します。
public static UpdateTableResponse convertToMRSCWithWitness( final DynamoDbClient dynamoDbClient, final String tableName, final Region replicaRegion, final Region witnessRegion) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (replicaRegion == null) { throw new IllegalArgumentException("Replica region cannot be null"); } if (witnessRegion == null) { throw new IllegalArgumentException("Witness region cannot be null"); } try { LOGGER.info("Converting table to MRSC with replica in " + replicaRegion.id() + " and witness in " + witnessRegion.id()); // Create replica update using ReplicationGroupUpdate ReplicationGroupUpdate replicaUpdate = ReplicationGroupUpdate.builder() .create(CreateReplicationGroupMemberAction.builder() .regionName(replicaRegion.id()) .build()) .build(); // Create witness update GlobalTableWitnessGroupUpdate witnessUpdate = GlobalTableWitnessGroupUpdate.builder() .create(CreateGlobalTableWitnessGroupMemberAction.builder() .regionName(witnessRegion.id()) .build()) .build(); UpdateTableRequest updateTableRequest = UpdateTableRequest.builder() .tableName(tableName) .replicaUpdates(List.of(replicaUpdate)) .globalTableWitnessUpdates(List.of(witnessUpdate)) .multiRegionConsistency(MultiRegionConsistency.STRONG) .build(); UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest); LOGGER.info("MRSC conversion initiated. Status: " + response.tableDescription().tableStatus()); LOGGER.info("UpdateTableResponse full object: " + response); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to convert table to MRSC: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to convert table to MRSC: " + tableName) .cause(e) .build(); } }
AWS SDK for Java 2.x を使用して、MRSC のグローバルテーブル設定を記述します。
public static DescribeTableResponse describeMRSCTable(final DynamoDbClient dynamoDbClient, final String tableName) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } try { LOGGER.info("Describing MRSC global table: " + tableName); DescribeTableRequest request = DescribeTableRequest.builder().tableName(tableName).build(); DescribeTableResponse response = dynamoDbClient.describeTable(request); LOGGER.info("Table status: " + response.table().tableStatus()); LOGGER.info("Multi-region consistency: " + response.table().multiRegionConsistency()); if (response.table().replicas() != null && !response.table().replicas().isEmpty()) { LOGGER.info("Number of replicas: " + response.table().replicas().size()); response.table() .replicas() .forEach(replica -> LOGGER.info( "Replica region: " + replica.regionName() + ", Status: " + replica.replicaStatus())); } if (response.table().globalTableWitnesses() != null && !response.table().globalTableWitnesses().isEmpty()) { LOGGER.info("Number of witnesses: " + response.table().globalTableWitnesses().size()); response.table() .globalTableWitnesses() .forEach(witness -> LOGGER.info( "Witness region: " + witness.regionName() + ", Status: " + witness.witnessStatus())); } return response; } catch (ResourceNotFoundException e) { LOGGER.severe("Table not found: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Table not found: " + tableName) .cause(e) .build(); } catch (DynamoDbException e) { LOGGER.severe("Failed to describe table: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to describe table: " + tableName) .cause(e) .build(); } }
AWS SDK for Java 2.x を使用して、MRSC の強力な整合性を検証するためのテスト項目を追加します。
public static PutItemResponse putTestItem( final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle, final String album, final String year) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (artist == null || artist.trim().isEmpty()) { throw new IllegalArgumentException("Artist cannot be null or empty"); } if (songTitle == null || songTitle.trim().isEmpty()) { throw new IllegalArgumentException("Song title cannot be null or empty"); } try { LOGGER.info("Adding test item to MRSC global table: " + tableName); Map<String, AttributeValue> item = new HashMap<>(); item.put("Artist", AttributeValue.builder().s(artist).build()); item.put("SongTitle", AttributeValue.builder().s(songTitle).build()); if (album != null && !album.trim().isEmpty()) { item.put("Album", AttributeValue.builder().s(album).build()); } if (year != null && !year.trim().isEmpty()) { item.put("Year", AttributeValue.builder().n(year).build()); } PutItemRequest putItemRequest = PutItemRequest.builder().tableName(tableName).item(item).build(); PutItemResponse response = dynamoDbClient.putItem(putItemRequest); LOGGER.info("Test item added successfully with strong consistency"); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to add test item to table: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to add test item to table: " + tableName) .cause(e) .build(); } }
AWS SDK for Java 2.x を使用して、MRSC レプリカから一貫した読み取りを持つ項目を読み取ります。
public static GetItemResponse getItemWithConsistentRead( final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (artist == null || artist.trim().isEmpty()) { throw new IllegalArgumentException("Artist cannot be null or empty"); } if (songTitle == null || songTitle.trim().isEmpty()) { throw new IllegalArgumentException("Song title cannot be null or empty"); } try { LOGGER.info("Reading item from MRSC global table with consistent read: " + tableName); Map<String, AttributeValue> key = new HashMap<>(); key.put("Artist", AttributeValue.builder().s(artist).build()); key.put("SongTitle", AttributeValue.builder().s(songTitle).build()); GetItemRequest getItemRequest = GetItemRequest.builder() .tableName(tableName) .key(key) .consistentRead(true) .build(); GetItemResponse response = dynamoDbClient.getItem(getItemRequest); if (response.hasItem()) { LOGGER.info("Item found with strong consistency - no wait time needed"); } else { LOGGER.info("Item not found"); } return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to read item from table: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to read item from table: " + tableName) .cause(e) .build(); } }
AWS SDK for Java 2.x を使用して、MRSC 保証を使用する条件付きの更新を実行します。
public static UpdateItemResponse performConditionalUpdate( final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle, final String rating) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (artist == null || artist.trim().isEmpty()) { throw new IllegalArgumentException("Artist cannot be null or empty"); } if (songTitle == null || songTitle.trim().isEmpty()) { throw new IllegalArgumentException("Song title cannot be null or empty"); } if (rating == null || rating.trim().isEmpty()) { throw new IllegalArgumentException("Rating cannot be null or empty"); } try { LOGGER.info("Performing conditional update on MRSC global table: " + tableName); Map<String, AttributeValue> key = new HashMap<>(); key.put("Artist", AttributeValue.builder().s(artist).build()); key.put("SongTitle", AttributeValue.builder().s(songTitle).build()); Map<String, String> expressionAttributeNames = new HashMap<>(); expressionAttributeNames.put("#rating", "Rating"); Map<String, AttributeValue> expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put( ":rating", AttributeValue.builder().n(rating).build()); UpdateItemRequest updateItemRequest = UpdateItemRequest.builder() .tableName(tableName) .key(key) .updateExpression("SET #rating = :rating") .conditionExpression("attribute_exists(Artist)") .expressionAttributeNames(expressionAttributeNames) .expressionAttributeValues(expressionAttributeValues) .build(); UpdateItemResponse response = dynamoDbClient.updateItem(updateItemRequest); LOGGER.info("Conditional update successful - demonstrates strong consistency"); return response; } catch (ConditionalCheckFailedException e) { LOGGER.warning("Conditional check failed: " + e.getMessage()); throw e; } catch (DynamoDbException e) { LOGGER.severe("Failed to perform conditional update: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to perform conditional update: " + tableName) .cause(e) .build(); } }
AWS SDK for Java 2.x を使用して、MRSC レプリカと監視がアクティブになるまで待ちます。
public static void waitForMRSCReplicasActive( final DynamoDbClient dynamoDbClient, final String tableName, final int maxWaitTimeSeconds) throws InterruptedException { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (maxWaitTimeSeconds <= 0) { throw new IllegalArgumentException("Max wait time must be positive"); } try { LOGGER.info("Waiting for MRSC replicas and witnesses to become active: " + tableName); final long startTime = System.currentTimeMillis(); final long maxWaitTimeMillis = maxWaitTimeSeconds * 1000L; int backoffSeconds = 5; // Start with 5 second intervals final int maxBackoffSeconds = 30; // Cap at 30 seconds while (System.currentTimeMillis() - startTime < maxWaitTimeMillis) { DescribeTableResponse response = describeMRSCTable(dynamoDbClient, tableName); boolean allActive = true; StringBuilder statusReport = new StringBuilder(); if (response.table().multiRegionConsistency() == null || !MultiRegionConsistency.STRONG .toString() .equals(response.table().multiRegionConsistency().toString())) { allActive = false; statusReport .append("MultiRegionConsistency: ") .append(response.table().multiRegionConsistency()) .append(" "); } if (response.table().replicas() == null || response.table().replicas().isEmpty()) { allActive = false; statusReport.append("No replicas found. "); } if (response.table().globalTableWitnesses() == null || response.table().globalTableWitnesses().isEmpty()) { allActive = false; statusReport.append("No witnesses found. "); } // Check table status if (!"ACTIVE".equals(response.table().tableStatus().toString())) { allActive = false; statusReport .append("Table: ") .append(response.table().tableStatus()) .append(" "); } // Check replica status if (response.table().replicas() != null) { for (var replica : response.table().replicas()) { if (!"ACTIVE".equals(replica.replicaStatus().toString())) { allActive = false; statusReport .append("Replica(") .append(replica.regionName()) .append("): ") .append(replica.replicaStatus()) .append(" "); } } } // Check witness status if (response.table().globalTableWitnesses() != null) { for (var witness : response.table().globalTableWitnesses()) { if (!"ACTIVE".equals(witness.witnessStatus().toString())) { allActive = false; statusReport .append("Witness(") .append(witness.regionName()) .append("): ") .append(witness.witnessStatus()) .append(" "); } } } if (allActive) { LOGGER.info("All MRSC replicas and witnesses are now active: " + tableName); return; } LOGGER.info("Waiting for MRSC components to become active. Status: " + statusReport.toString()); LOGGER.info("Next check in " + backoffSeconds + " seconds..."); tempWait(backoffSeconds); // Exponential backoff with cap backoffSeconds = Math.min(backoffSeconds * 2, maxBackoffSeconds); } throw DynamoDbException.builder() .message("Timeout waiting for MRSC replicas to become active after " + maxWaitTimeSeconds + " seconds") .build(); } catch (DynamoDbException | InterruptedException e) { LOGGER.severe("Failed to wait for MRSC replicas to become active: " + tableName + " - " + e.getMessage()); throw e; } }
AWS SDK for Java 2.x を使用して、MRSC レプリカと監視をクリーンアップします。
public static UpdateTableResponse cleanupMRSCReplicas( final DynamoDbClient dynamoDbClient, final String tableName, final Region replicaRegion, final Region witnessRegion) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (replicaRegion == null) { throw new IllegalArgumentException("Replica region cannot be null"); } if (witnessRegion == null) { throw new IllegalArgumentException("Witness region cannot be null"); } try { LOGGER.info("Cleaning up MRSC replicas and witnesses for table: " + tableName); // Remove replica using ReplicationGroupUpdate ReplicationGroupUpdate replicaUpdate = ReplicationGroupUpdate.builder() .delete(DeleteReplicationGroupMemberAction.builder() .regionName(replicaRegion.id()) .build()) .build(); // Remove witness GlobalTableWitnessGroupUpdate witnessUpdate = GlobalTableWitnessGroupUpdate.builder() .delete(DeleteGlobalTableWitnessGroupMemberAction.builder() .regionName(witnessRegion.id()) .build()) .build(); UpdateTableRequest updateTableRequest = UpdateTableRequest.builder() .tableName(tableName) .replicaUpdates(List.of(replicaUpdate)) .globalTableWitnessUpdates(List.of(witnessUpdate)) .build(); UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest); LOGGER.info("MRSC cleanup initiated - removing replica and witness. Response: " + response); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to cleanup MRSC replicas: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to cleanup MRSC replicas: " + tableName) .cause(e) .build(); } }
AWS SDK for Java 2.x を使用して、MRSC ワークフローのデモンストレーションを完了します。
public static void demonstrateCompleteMRSCWorkflow( final DynamoDbClient primaryClient, final DynamoDbClient replicaClient, final String tableName, final Region replicaRegion, final Region witnessRegion) throws InterruptedException { if (primaryClient == null) { throw new IllegalArgumentException("Primary DynamoDB client cannot be null"); } if (replicaClient == null) { throw new IllegalArgumentException("Replica DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (replicaRegion == null) { throw new IllegalArgumentException("Replica region cannot be null"); } if (witnessRegion == null) { throw new IllegalArgumentException("Witness region cannot be null"); } try { LOGGER.info("=== Starting Complete MRSC Workflow Demonstration ==="); // Step 1: Create an empty single-Region table LOGGER.info("Step 1: Creating empty single-Region table"); createRegionalTable(primaryClient, tableName); // Use the existing GlobalTableOperations method for basic table waiting LOGGER.info("Intermediate step: Waiting for table [" + tableName + "] to become active before continuing"); GlobalTableOperations.waitForTableActive(primaryClient, tableName); // Step 2: Convert to MRSC with replica and witness LOGGER.info("Step 2: Converting to MRSC with replica and witness"); convertToMRSCWithWitness(primaryClient, tableName, replicaRegion, witnessRegion); // Wait for MRSC conversion to complete using MRSC-specific waiter LOGGER.info("Waiting for MRSC conversion to complete..."); waitForMRSCReplicasActive(primaryClient, tableName); LOGGER.info("Intermediate step: Waiting for table [" + tableName + "] to become active before continuing"); GlobalTableOperations.waitForTableActive(primaryClient, tableName); // Step 3: Verify MRSC configuration LOGGER.info("Step 3: Verifying MRSC configuration"); describeMRSCTable(primaryClient, tableName); // Step 4: Test strong consistency with data operations LOGGER.info("Step 4: Testing strong consistency with data operations"); // Add test item to primary region putTestItem(primaryClient, tableName, "The Beatles", "Hey Jude", "The Beatles 1967-1970", "1968"); // Immediately read from replica region (no wait needed with MRSC) LOGGER.info("Reading from replica region immediately (strong consistency):"); GetItemResponse getResponse = getItemWithConsistentRead(replicaClient, tableName, "The Beatles", "Hey Jude"); if (getResponse.hasItem()) { LOGGER.info("✓ Strong consistency verified - item immediately available in replica region"); } else { LOGGER.warning("✗ Item not found in replica region"); } // Test conditional update from replica region LOGGER.info("Testing conditional update from replica region:"); performConditionalUpdate(replicaClient, tableName, "The Beatles", "Hey Jude", "5"); LOGGER.info("✓ Conditional update successful - demonstrates strong consistency"); // Step 5: Cleanup LOGGER.info("Step 5: Cleaning up resources"); cleanupMRSCReplicas(primaryClient, tableName, replicaRegion, witnessRegion); // Wait for cleanup to complete using basic table waiter LOGGER.info("Waiting for replica cleanup to complete..."); GlobalTableOperations.waitForTableActive(primaryClient, tableName); // "Halt" until replica/witness cleanup is complete DescribeTableResponse cleanupVerification = describeMRSCTable(primaryClient, tableName); int backoffSeconds = 5; // Start with 5 second intervals while (cleanupVerification.table().multiRegionConsistency() != null) { LOGGER.info("Waiting additional time (" + backoffSeconds + " seconds) for MRSC cleanup to complete..."); tempWait(backoffSeconds); // Exponential backoff with cap backoffSeconds = Math.min(backoffSeconds * 2, 30); cleanupVerification = describeMRSCTable(primaryClient, tableName); } // Delete the primary table deleteTable(primaryClient, tableName); LOGGER.info("=== MRSC Workflow Demonstration Complete ==="); LOGGER.info(""); LOGGER.info("Key benefits of Multi-Region Strong Consistency (MRSC):"); LOGGER.info("- Immediate consistency across all regions (no eventual consistency delays)"); LOGGER.info("- Simplified application logic (no need to handle eventual consistency)"); LOGGER.info("- Support for conditional writes and transactions across regions"); LOGGER.info("- Consistent read operations from any region without waiting"); } catch (DynamoDbException | InterruptedException e) { LOGGER.severe("MRSC workflow failed: " + e.getMessage()); throw e; } }
-
API の詳細については、『AWS SDK for Java 2.x API リファレンス』の以下のトピックを参照してください。
-